' +
- * hljs.highlight(lang, str, true).value +
- * '
';
- * } catch (__) {}
- * }
- *
- * return '' + md.utils.escapeHtml(str) + '
';
- * }
- * });
- * ```
- *
- **/
-function MarkdownIt(presetName, options) {
- if (!(this instanceof MarkdownIt)) {
- return new MarkdownIt(presetName, options);
- }
+ 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++
+ }
- if (!options) {
- if (!utils.isString(presetName)) {
- options = presetName || {};
- presetName = 'default';
- }
- }
+ return result
+ }
+ },
+ { '../common/utils': 4 },
+ ],
+ 9: [
+ function (require, module, exports) {
+ // Main parser 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 mdurl = require('mdurl')
+ var punycode = require('punycode')
+
+ var config = {
+ default: require('./presets/default'),
+ zero: require('./presets/zero'),
+ commonmark: require('./presets/commonmark'),
+ }
- /**
- * 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();
-
- /**
- * MarkdownIt#validateLink(url) -> Boolean
- *
- * Link validation function. CommonMark allows too much in links. By default
- * we disable `javascript:`, `vbscript:`, `file:` schemas, and almost all `data:...` schemas
- * except some embedded image types.
- *
- * You can change this behaviour:
- *
- * ```javascript
- * var md = require('markdown-it')();
- * // enable everything
- * md.validateLink = function () { return true; }
- * ```
- **/
- this.validateLink = validateLink;
-
- /**
- * MarkdownIt#normalizeLink(url) -> String
- *
- * Function used to encode link url to a machine-readable format,
- * which includes url-encoding, punycode, etc.
- **/
- this.normalizeLink = normalizeLink;
-
- /**
- * MarkdownIt#normalizeLinkText(url) -> String
- *
- * Function used to decode link url to a human-readable format`
- **/
- this.normalizeLinkText = normalizeLinkText;
-
-
- // 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 = utils.assign({}, 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 will - 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'); }
- }
+ ////////////////////////////////////////////////////////////////////////////////
+ //
+ // This validator can prohibit more than really needed to prevent XSS. It's a
+ // tradeoff to keep code simple and to be secure by default.
+ //
+ // If you need different setup - override validator method as you wish. Or
+ // replace it with dummy function and use external sanitizer.
+ //
- if (!presets) { throw new Error('Wrong `markdown-it` preset, can\'t be empty'); }
+ var BAD_PROTO_RE = /^(vbscript|javascript|file|data):/
+ var GOOD_DATA_RE = /^data:image\/(gif|png|jpeg|webp);/
- if (presets.options) { self.set(presets.options); }
+ function validateLink(url) {
+ // url should be normalized at this point, and existing entities are decoded
+ var str = url.trim().toLowerCase()
- if (presets.components) {
- Object.keys(presets.components).forEach(function (name) {
- if (presets.components[name].rules) {
- self[name].ruler.enableOnly(presets.components[name].rules);
- }
- if (presets.components[name].rules2) {
- self[name].ruler2.enableOnly(presets.components[name].rules2);
- }
- });
- }
- 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);
-
- result = result.concat(this.inline.ruler2.enable(list, true));
-
- 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 BAD_PROTO_RE.test(str) ? (GOOD_DATA_RE.test(str) ? true : false) : true
+ }
- return this;
-};
+ ////////////////////////////////////////////////////////////////////////////////
+
+ var RECODE_HOSTNAME_FOR = ['http:', 'https:', 'mailto:']
+
+ function normalizeLink(url) {
+ var parsed = mdurl.parse(url, true)
+
+ if (parsed.hostname) {
+ // Encode hostnames in urls like:
+ // `http://host/`, `https://host/`, `mailto:user@host`, `//host/`
+ //
+ // We don't encode unknown schemas, because it's likely that we encode
+ // something we shouldn't (e.g. `skype:name` treated as `skype:host`)
+ //
+ if (!parsed.protocol || RECODE_HOSTNAME_FOR.indexOf(parsed.protocol) >= 0) {
+ try {
+ parsed.hostname = punycode.toASCII(parsed.hostname)
+ } catch (er) {
+ /**/
+ }
+ }
+ }
+ return mdurl.encode(mdurl.format(parsed))
+ }
-/** 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 = [];
+ function normalizeLinkText(url) {
+ var parsed = mdurl.parse(url, true)
+
+ if (parsed.hostname) {
+ // Encode hostnames in urls like:
+ // `http://host/`, `https://host/`, `mailto:user@host`, `//host/`
+ //
+ // We don't encode unknown schemas, because it's likely that we encode
+ // something we shouldn't (e.g. `skype:name` treated as `skype:host`)
+ //
+ if (!parsed.protocol || RECODE_HOSTNAME_FOR.indexOf(parsed.protocol) >= 0) {
+ try {
+ parsed.hostname = punycode.toUnicode(parsed.hostname)
+ } catch (er) {
+ /**/
+ }
+ }
+ }
- if (!Array.isArray(list)) { list = [ list ]; }
+ return mdurl.decode(mdurl.format(parsed))
+ }
- [ 'core', 'block', 'inline' ].forEach(function (chain) {
- result = result.concat(this[chain].ruler.disable(list, true));
- }, this);
+ /**
+ * 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
+ * (`' +
+ * hljs.highlight(lang, str, true).value +
+ * '
';
+ * } catch (__) {}
+ * }
+ *
+ * return '' + md.utils.escapeHtml(str) + '
';
+ * }
+ * });
+ * ```
+ *
+ **/
+ function MarkdownIt(presetName, options) {
+ if (!(this instanceof MarkdownIt)) {
+ return new MarkdownIt(presetName, options)
+ }
- result = result.concat(this.inline.ruler2.disable(list, true));
+ if (!options) {
+ if (!utils.isString(presetName)) {
+ options = presetName || {}
+ presetName = 'default'
+ }
+ }
- var missed = list.filter(function (name) { return result.indexOf(name) < 0; });
+ /**
+ * 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()
+
+ /**
+ * MarkdownIt#validateLink(url) -> Boolean
+ *
+ * Link validation function. CommonMark allows too much in links. By default
+ * we disable `javascript:`, `vbscript:`, `file:` schemas, and almost all `data:...` schemas
+ * except some embedded image types.
+ *
+ * You can change this behaviour:
+ *
+ * ```javascript
+ * var md = require('markdown-it')();
+ * // enable everything
+ * md.validateLink = function () { return true; }
+ * ```
+ **/
+ this.validateLink = validateLink
+
+ /**
+ * MarkdownIt#normalizeLink(url) -> String
+ *
+ * Function used to encode link url to a machine-readable format,
+ * which includes url-encoding, punycode, etc.
+ **/
+ this.normalizeLink = normalizeLink
+
+ /**
+ * MarkdownIt#normalizeLinkText(url) -> String
+ *
+ * Function used to decode link url to a human-readable format`
+ **/
+ this.normalizeLinkText = normalizeLinkText
+
+ // 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 = utils.assign({}, helpers)
+
+ this.options = {}
+ this.configure(presetName)
+
+ if (options) {
+ this.set(options)
+ }
+ }
- 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 return 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 the 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) {
- if (typeof src !== 'string') {
- throw new Error('Input data should be a String');
- }
+ /** 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
+ }
- 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":4,"./helpers":5,"./parser_block":10,"./parser_core":11,"./parser_inline":12,"./presets/commonmark":13,"./presets/default":14,"./presets/zero":15,"./renderer":16,"linkify-it":53,"mdurl":58,"punycode":60}],10:[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. - [ 'table', require('./rules_block/table'), [ 'paragraph', 'reference' ] ], - [ 'code', require('./rules_block/code') ], - [ 'fence', require('./rules_block/fence'), [ 'paragraph', 'reference', 'blockquote', 'list' ] ], - [ 'blockquote', require('./rules_block/blockquote'), [ 'paragraph', 'reference', 'blockquote', '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' ] ], - [ '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.sCount[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; - } + /** 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 will - 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') + } + } - // Try all possible rules. - // On success, rule should: - // - // - update `state.line` - // - update `state.tokens` - // - return true + if (!presets) { + throw new Error("Wrong `markdown-it` preset, can't be empty") + } - for (i = 0; i < len; i++) { - ok = rules[i](state, line, endLine, false); - if (ok) { break; } - } + if (presets.options) { + self.set(presets.options) + } - // set state.tight if we had an empty line before current tag - // i.e. latest empty line should not count - state.tight = !hasEmptyLines; + if (presets.components) { + Object.keys(presets.components).forEach(function (name) { + if (presets.components[name].rules) { + self[name].ruler.enableOnly(presets.components[name].rules) + } + if (presets.components[name].rules2) { + self[name].ruler2.enableOnly(presets.components[name].rules2) + } + }) + } + return this + } - // paragraph might "eat" one newline after it in nested lists - if (state.isEmpty(state.line - 1)) { - hasEmptyLines = true; - } + /** 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] + } - line = state.line; + ;['core', 'block', 'inline'].forEach(function (chain) { + result = result.concat(this[chain].ruler.enable(list, true)) + }, this) - if (line < endLine && state.isEmpty(line)) { - hasEmptyLines = true; - line++; - state.line = line; - } - } -}; + result = result.concat(this.inline.ruler2.enable(list, true)) + var missed = list.filter(function (name) { + return result.indexOf(name) < 0 + }) -/** - * 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 (missed.length && !ignoreInvalid) { + throw new Error('MarkdownIt. Failed to enable unknown rule(s): ' + missed) + } - if (!src) { return; } + return this + } - state = new this.State(src, md, env, outTokens); + /** 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] + } - this.tokenize(state, state.line, state.lineMax); -}; + ;['core', 'block', 'inline'].forEach(function (chain) { + result = result.concat(this[chain].ruler.disable(list, true)) + }, this) + result = result.concat(this.inline.ruler2.disable(list, true)) -ParserBlock.prototype.State = require('./rules_block/state_block'); + 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 + } -module.exports = ParserBlock; + /** 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 + } -},{"./ruler":17,"./rules_block/blockquote":18,"./rules_block/code":19,"./rules_block/fence":20,"./rules_block/heading":21,"./rules_block/hr":22,"./rules_block/html_block":23,"./rules_block/lheading":24,"./rules_block/list":25,"./rules_block/paragraph":26,"./rules_block/reference":27,"./rules_block/state_block":28,"./rules_block/table":29}],11:[function(require,module,exports){ -/** internal - * class Core - * - * Top-level rules executor. Glues block/inline parsers and does intermediate - * transformations. - **/ -'use strict'; + /** internal + * MarkdownIt.parse(src, env) -> Array + * - src (String): source string + * - env (Object): environment sandbox + * + * Parse input string and return 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 the 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) { + if (typeof src !== 'string') { + throw new Error('Input data should be a String') + } + var state = new this.core.State(src, this, env) -var Ruler = require('./ruler'); + this.core.process(state) + return state.tokens + } -var _rules = [ - [ 'normalize', require('./rules_core/normalize') ], - [ 'block', require('./rules_core/block') ], - [ 'inline', require('./rules_core/inline') ], - [ 'linkify', require('./rules_core/linkify') ], - [ 'replacements', require('./rules_core/replacements') ], - [ 'smartquotes', require('./rules_core/smartquotes') ] -]; + /** + * 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 + } -/** - * new Core() - **/ -function Core() { - /** - * Core#ruler -> Ruler - * - * [[Ruler]] instance. Keep configuration of core rules. - **/ - this.ruler = new Ruler(); + /** + * 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)
+ }
- for (var i = 0; i < _rules.length; i++) {
- this.ruler.push(_rules[i][0], _rules[i][1]);
- }
-}
+ module.exports = MarkdownIt
+ },
+ {
+ './common/utils': 4,
+ './helpers': 5,
+ './parser_block': 10,
+ './parser_core': 11,
+ './parser_inline': 12,
+ './presets/commonmark': 13,
+ './presets/default': 14,
+ './presets/zero': 15,
+ './renderer': 16,
+ 'linkify-it': 53,
+ mdurl: 58,
+ punycode: 60,
+ },
+ ],
+ 10: [
+ 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.
+ ['table', require('./rules_block/table'), ['paragraph', 'reference']],
+ ['code', require('./rules_block/code')],
+ ['fence', require('./rules_block/fence'), ['paragraph', 'reference', 'blockquote', 'list']],
+ ['blockquote', require('./rules_block/blockquote'), ['paragraph', 'reference', 'blockquote', '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']],
+ ['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.sCount[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 if 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++
+ state.line = line
+ }
+ }
+ }
-/**
- * Core.process(state)
- *
- * Executes core chain rules.
- **/
-Core.prototype.process = function (state) {
- var i, l, rules;
+ /**
+ * 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
- rules = this.ruler.getRules('');
+ if (!src) {
+ return
+ }
- for (i = 0, l = rules.length; i < l; i++) {
- rules[i](state);
- }
-};
+ state = new this.State(src, md, env, outTokens)
-Core.prototype.State = require('./rules_core/state_core');
+ this.tokenize(state, state.line, state.lineMax)
+ }
+ ParserBlock.prototype.State = require('./rules_block/state_block')
+
+ module.exports = ParserBlock
+ },
+ {
+ './ruler': 17,
+ './rules_block/blockquote': 18,
+ './rules_block/code': 19,
+ './rules_block/fence': 20,
+ './rules_block/heading': 21,
+ './rules_block/hr': 22,
+ './rules_block/html_block': 23,
+ './rules_block/lheading': 24,
+ './rules_block/list': 25,
+ './rules_block/paragraph': 26,
+ './rules_block/reference': 27,
+ './rules_block/state_block': 28,
+ './rules_block/table': 29,
+ },
+ ],
+ 11: [
+ 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')],
+ ['linkify', require('./rules_core/linkify')],
+ ['replacements', require('./rules_core/replacements')],
+ ['smartquotes', require('./rules_core/smartquotes')],
+ ]
+
+ /**
+ * 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])
+ }
+ }
-module.exports = Core;
+ /**
+ * Core.process(state)
+ *
+ * Executes core chain rules.
+ **/
+ Core.prototype.process = function (state) {
+ var i, l, rules
-},{"./ruler":17,"./rules_core/block":30,"./rules_core/inline":31,"./rules_core/linkify":32,"./rules_core/normalize":33,"./rules_core/replacements":34,"./rules_core/smartquotes":35,"./rules_core/state_core":36}],12:[function(require,module,exports){
-/** internal
- * class ParserInline
- *
- * Tokenizes paragraph content.
- **/
-'use strict';
+ rules = this.ruler.getRules('')
+ for (i = 0, l = rules.length; i < l; i++) {
+ rules[i](state)
+ }
+ }
-var Ruler = require('./ruler');
+ Core.prototype.State = require('./rules_core/state_core')
+
+ module.exports = Core
+ },
+ {
+ './ruler': 17,
+ './rules_core/block': 30,
+ './rules_core/inline': 31,
+ './rules_core/linkify': 32,
+ './rules_core/normalize': 33,
+ './rules_core/replacements': 34,
+ './rules_core/smartquotes': 35,
+ './rules_core/state_core': 36,
+ },
+ ],
+ 12: [
+ function (require, module, exports) {
+ /** internal
+ * class ParserInline
+ *
+ * Tokenizes paragraph content.
+ **/
+ 'use strict'
+
+ var Ruler = require('./ruler')
+
+ ////////////////////////////////////////////////////////////////////////////////
+ // 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').tokenize],
+ ['emphasis', require('./rules_inline/emphasis').tokenize],
+ ['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 _rules2 = [
+ ['balance_pairs', require('./rules_inline/balance_pairs')],
+ ['strikethrough', require('./rules_inline/strikethrough').postProcess],
+ ['emphasis', require('./rules_inline/emphasis').postProcess],
+ ['text_collapse', require('./rules_inline/text_collapse')],
+ ]
+
+ /**
+ * new ParserInline()
+ **/
+ function ParserInline() {
+ var i
+
+ /**
+ * ParserInline#ruler -> Ruler
+ *
+ * [[Ruler]] instance. Keep configuration of inline rules.
+ **/
+ this.ruler = new Ruler()
+
+ for (i = 0; i < _rules.length; i++) {
+ this.ruler.push(_rules[i][0], _rules[i][1])
+ }
+ /**
+ * ParserInline#ruler2 -> Ruler
+ *
+ * [[Ruler]] instance. Second ruler used for post-processing
+ * (e.g. in emphasis-like rules).
+ **/
+ this.ruler2 = new Ruler()
-////////////////////////////////////////////////////////////////////////////////
-// Parser rules
+ for (i = 0; i < _rules2.length; i++) {
+ this.ruler2.push(_rules2[i][0], _rules2[i][1])
+ }
+ }
-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').tokenize ],
- [ 'emphasis', require('./rules_inline/emphasis').tokenize ],
- [ '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') ]
-];
+ // Skip single token by running all rules in validation mode;
+ // returns `true` if any rule reported success
+ //
+ ParserInline.prototype.skipToken = function (state) {
+ var ok,
+ i,
+ pos = state.pos,
+ rules = this.ruler.getRules(''),
+ len = rules.length,
+ maxNesting = state.md.options.maxNesting,
+ cache = state.cache
+
+ if (typeof cache[pos] !== 'undefined') {
+ state.pos = cache[pos]
+ return
+ }
-var _rules2 = [
- [ 'balance_pairs', require('./rules_inline/balance_pairs') ],
- [ 'strikethrough', require('./rules_inline/strikethrough').postProcess ],
- [ 'emphasis', require('./rules_inline/emphasis').postProcess ],
- [ 'text_collapse', require('./rules_inline/text_collapse') ]
-];
+ if (state.level < maxNesting) {
+ for (i = 0; i < len; i++) {
+ // Increment state.level and decrement it later to limit recursion.
+ // It's harmless to do here, because no tokens are created. But ideally,
+ // we'd need a separate private state variable for this purpose.
+ //
+ state.level++
+ ok = rules[i](state, true)
+ state.level--
+
+ if (ok) {
+ break
+ }
+ }
+ } else {
+ // Too much nesting, just skip until the end of the paragraph.
+ //
+ // NOTE: this will cause links to behave incorrectly in the following case,
+ // when an amount of `[` is exactly equal to `maxNesting + 1`:
+ //
+ // [[[[[[[[[[[[[[[[[[[[[foo]()
+ //
+ // TODO: remove this workaround when CM standard will allow nested links
+ // (we can replace it by preventing links from being parsed in
+ // validation mode)
+ //
+ state.pos = state.posMax
+ }
+ if (!ok) {
+ state.pos++
+ }
+ cache[pos] = state.pos
+ }
-/**
- * new ParserInline()
- **/
-function ParserInline() {
- var i;
+ // 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++]
+ }
- /**
- * ParserInline#ruler -> Ruler
- *
- * [[Ruler]] instance. Keep configuration of inline rules.
- **/
- this.ruler = new Ruler();
+ if (state.pending) {
+ state.pushPending()
+ }
+ }
- for (i = 0; i < _rules.length; i++) {
- this.ruler.push(_rules[i][0], _rules[i][1]);
- }
+ /**
+ * ParserInline.parse(str, md, env, outTokens)
+ *
+ * Process input string and push inline tokens into `outTokens`
+ **/
+ ParserInline.prototype.parse = function (str, md, env, outTokens) {
+ var i, rules, len
+ var state = new this.State(str, md, env, outTokens)
- /**
- * ParserInline#ruler2 -> Ruler
- *
- * [[Ruler]] instance. Second ruler used for post-processing
- * (e.g. in emphasis-like rules).
- **/
- this.ruler2 = new Ruler();
+ this.tokenize(state)
- for (i = 0; i < _rules2.length; i++) {
- this.ruler2.push(_rules2[i][0], _rules2[i][1]);
- }
-}
+ rules = this.ruler2.getRules('')
+ len = rules.length
+ for (i = 0; i < len; i++) {
+ rules[i](state)
+ }
+ }
-// Skip single token by running all rules in validation mode;
-// returns `true` if any rule reported success
-//
-ParserInline.prototype.skipToken = function (state) {
- var ok, i, pos = state.pos,
- rules = this.ruler.getRules(''),
- len = rules.length,
- maxNesting = state.md.options.maxNesting,
- cache = state.cache;
+ ParserInline.prototype.State = require('./rules_inline/state_inline')
+
+ module.exports = ParserInline
+ },
+ {
+ './ruler': 17,
+ './rules_inline/autolink': 37,
+ './rules_inline/backticks': 38,
+ './rules_inline/balance_pairs': 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,
+ './rules_inline/text_collapse': 50,
+ },
+ ],
+ 13: [
+ 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. Could be either a String or an Array.
+ //
+ // For example, you can use '«»„“' for Russian, '„“‚‘' for German,
+ // and ['«\xA0', '\xA0»', '‹\xA0', '\xA0›'] for French (including nbsp).
+ quotes: '\u201c\u201d\u2018\u2019' /* “”‘’ */,
+
+ // Highlighter function. Should return escaped HTML,
+ // or '' if the source string is not changed and should be escaped externaly.
+ // If result starts with
' + escapeHtml(tokens[idx].content) + '
'
+ }
- state.pending += state.src[state.pos++];
- }
+ default_rules.code_block = function (tokens, idx, options, env, slf) {
+ var token = tokens[idx]
- if (state.pending) {
- state.pushPending();
- }
-};
+ return '' + escapeHtml(tokens[idx].content) + '
\n'
+ }
+ default_rules.fence = function (tokens, idx, options, env, slf) {
+ var token = tokens[idx],
+ info = token.info ? unescapeAll(token.info).trim() : '',
+ langName = '',
+ highlighted,
+ i,
+ tmpAttrs,
+ tmpToken
+
+ if (info) {
+ langName = info.split(/\s+/g)[0]
+ }
-/**
- * ParserInline.parse(str, md, env, outTokens)
- *
- * Process input string and push inline tokens into `outTokens`
- **/
-ParserInline.prototype.parse = function (str, md, env, outTokens) {
- var i, rules, len;
- var state = new this.State(str, md, env, outTokens);
+ if (options.highlight) {
+ highlighted = options.highlight(token.content, langName) || escapeHtml(token.content)
+ } else {
+ highlighted = escapeHtml(token.content)
+ }
- this.tokenize(state);
+ if (highlighted.indexOf('' + highlighted + '
\n'
+ }
- for (i = 0; i < len; i++) {
- rules[i](state);
- }
-};
+ return '' + highlighted + '
\n'
+ }
+ default_rules.image = function (tokens, idx, options, env, slf) {
+ var token = tokens[idx]
-ParserInline.prototype.State = require('./rules_inline/state_inline');
+ // "alt" attr MUST be set, even if empty. Because it's mandatory and
+ // should be placed on proper position for tests.
+ //
+ // Replace content with actual value
+ token.attrs[token.attrIndex('alt')][1] = slf.renderInlineAsText(token.children, options, env)
-module.exports = ParserInline;
+ return slf.renderToken(tokens, idx, options)
+ }
-},{"./ruler":17,"./rules_inline/autolink":37,"./rules_inline/backticks":38,"./rules_inline/balance_pairs":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,"./rules_inline/text_collapse":50}],13:[function(require,module,exports){
-// Commonmark default options
+ default_rules.hardbreak = function (tokens, idx, options /*, env */) {
+ return options.xhtmlOut ? '' +
- escapeHtml(tokens[idx].content) +
- '
';
-};
+ /**
+ * 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 || [],
+ })
-default_rules.code_block = function (tokens, idx, options, env, slf) {
- var token = tokens[idx];
+ this.__cache__ = null
+ }
- return '' +
- escapeHtml(tokens[idx].content) +
- '
\n';
-};
+ /**
+ * 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('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]
+ }
-default_rules.fence = function (tokens, idx, options, env, slf) {
- var token = tokens[idx],
- info = token.info ? unescapeAll(token.info).trim() : '',
- langName = '',
- highlighted, i, tmpAttrs, tmpToken;
+ var result = []
- if (info) {
- langName = info.split(/\s+/g)[0];
- }
+ // Search by name and enable
+ list.forEach(function (name) {
+ var idx = this.__find__(name)
- if (options.highlight) {
- highlighted = options.highlight(token.content, langName) || escapeHtml(token.content);
- } else {
- highlighted = escapeHtml(token.content);
- }
+ if (idx < 0) {
+ if (ignoreInvalid) {
+ return
+ }
+ throw new Error('Rules manager: invalid rule name ' + name)
+ }
+ this.__rules__[idx].enabled = true
+ result.push(name)
+ }, this)
- if (highlighted.indexOf(''
- + highlighted
- + '
\n';
- }
+ /**
+ * 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 = []
- return ''
- + highlighted
- + '
\n';
-};
-
-
-default_rules.image = function (tokens, idx, options, env, slf) {
- var token = tokens[idx];
-
- // "alt" attr MUST be set, even if empty. Because it's mandatory and
- // should be placed on proper position for tests.
- //
- // Replace content with actual value
-
- token.attrs[token.attrIndex('alt')][1] =
- slf.renderInlineAsText(token.children, options, env);
-
- return slf.renderToken(tokens, idx, options);
-};
-
-
-default_rules.hardbreak = function (tokens, idx, options /*, env */) {
- return options.xhtmlOut ? '${i18n('GURPS.dropResolve')}
`, - buttons: { - one: { - icon: '', - label: `${i18n('GURPS.dropBefore')}`, - callback: async () => { - if (!isSrcFirst) { - await this._removeKey(sourceKey) - await this._insertBeforeKey(targetkey, object) - } else { - await this._insertBeforeKey(targetkey, object) - await this._removeKey(sourceKey) - } - }, - }, - two: { - icon: '', - label: `${i18n('GURPS.dropInside')}`, - callback: async () => { - let key = targetkey + '.contains.' + zeroFill(0) - if (!isSrcFirst) { - await this._removeKey(sourceKey) - await this._insertBeforeKey(key, object) - } else { - await this._insertBeforeKey(key, object) - await this._removeKey(sourceKey) - } - }, - }, - }, - default: 'one', - }) - d.render(true) - } - } - } - - async _insertBeforeKey(targetKey, element) { - // target key is the whole path, like 'data.melee.00001' - let components = targetKey.split('.') - - let index = parseInt(components.pop()) - let path = components.join('.') - - let object = GURPS.decode(this.actor, path) - let array = objectToArray(object) - - // Delete the whole object. - let last = components.pop() - let t = `${components.join('.')}.-=${last}` - await this.actor.internalUpdate({ [t]: null }) - - // Insert the element into the array. - array.splice(index, 0, element) - - // Convert back to an object - object = arrayToObject(array, 5) - - // update the actor - await this.actor.internalUpdate({ [path]: object }, { diff: false }) - } - - async _removeKey(sourceKey) { - // source key is the whole path, like 'data.melee.00001' - let components = sourceKey.split('.') - - let index = parseInt(components.pop()) - let path = components.join('.') - - let object = GURPS.decode(this.actor, path) - let array = objectToArray(object) - - // Delete the whole object. - let last = components.pop() - let t = `${components.join('.')}.-=${last}` - await this.actor.internalUpdate({ [t]: null }) - - // Remove the element from the array - array.splice(index, 1) - - // Convert back to an object - object = arrayToObject(array, 5) - - // update the actor - await this.actor.internalUpdate({ [path]: object }, { diff: false }) - } - - _onfocus(ev) { - ev.preventDefault() - GURPS.SetLastActor(this.actor) - } - - /** @override */ - setPosition(options = {}) { - const position = super.setPosition(options) - const sheetBody = this.element.find('.sheet-body') - if (!!position.height) { - const bodyHeight = position.height - 192 - sheetBody.css('height', bodyHeight) - } - return position - } - - get title() { - const t = this.actor.name - const sheet = this.actor.getFlag('core', 'sheetClass') - return sheet === 'gurps.GurpsActorEditorSheet' ? '**** Editing: ' + t + ' ****' : t - } - - _getHeaderButtons() { - let buttons = super._getHeaderButtons() - - // Token Configuration - if (this.options.editable && isConfigurationAllowed(this.actor)) { - buttons = this.getCustomHeaderButtons().concat(buttons) - } - return buttons - } - - /** - * Override this to change the buttons appended to the actor sheet title bar. - */ - getCustomHeaderButtons() { - const sheet = this.actor.getFlag('core', 'sheetClass') - const isEditor = sheet === 'gurps.GurpsActorEditorSheet' - const altsheet = game.settings.get(settings.SYSTEM_NAME, settings.SETTING_ALT_SHEET) - - const isFull = sheet === undefined || sheet === 'gurps.GurpsActorSheet' - let b = [ - { - label: isFull ? altsheet : 'Full View', - class: 'toggle', - icon: 'fas fa-exchange-alt', - onclick: ev => this._onToggleSheet(ev, altsheet), - }, - ] - - if (!game.settings.get(settings.SYSTEM_NAME, settings.SETTING_BLOCK_IMPORT) || game.user.isTrusted) - b.push({ - label: 'Import', - class: 'import', - icon: 'fas fa-file-import', - onclick: ev => this._onFileImport(ev), - }) - - if (!isEditor) { - b.push({ - label: 'Editor', - class: 'edit', - icon: 'fas fa-edit', - onclick: ev => this._onOpenEditor(ev), - }) - } - return b - } - - async _onFileImport(event) { - event.preventDefault() - this.actor.importCharacter() - } - - async _onToggleSheet(event, altsheet) { - event.preventDefault() - let newSheet = Object.values(CONFIG.Actor.sheetClasses['character']).filter(s => s.label == altsheet)[0].id - - const original = - this.actor.getFlag('core', 'sheetClass') || - Object.values(CONFIG.Actor.sheetClasses['character']).filter(s => s.default)[0].id - console.log('original: ' + original) - - if (original != 'gurps.GurpsActorSheet') newSheet = 'gurps.GurpsActorSheet' - if (event.shiftKey) - // Hold down the shift key for Simplified - newSheet = 'gurps.GurpsActorSimplifiedSheet' - if (game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.CONTROL)) - // Hold down the Ctrl key (Command on Mac) for Simplified - newSheet = 'gurps.GurpsActorNpcSheet' - - this.actor.openSheet(newSheet) - } - - async _onOpenEditor(event) { - event.preventDefault() - this.actor.openSheet('gurps.GurpsActorEditorSheet') - } - - async _onRightClickGurpslink(event) { - event.preventDefault() - event.stopImmediatePropagation() // Since this may occur in note or a list (which has its own RMB handler) - let el = event.currentTarget - let action = el.dataset.action - if (!!action) { - action = JSON.parse(atou(action)) - if (action.type === 'damage' || action.type === 'deriveddamage') { - GURPS.resolveDamageRoll(event, this.actor, action.orig, action.overridetxt, game.user.isGM, true) - } else { - GURPS.whisperOtfToOwner(action.orig, action.overridetxt, event, action, this.actor) // only offer blind rolls for things that can be blind, No need to offer blind roll if it is already blind - } - } - } - - async _onRightClickPdf(event) { - event.preventDefault() - let el = event.currentTarget - GURPS.whisperOtfToOwner('PDF:' + el.innerText, null, event, false, this.actor) - } - - async _onRightClickGmod(event) { - event.preventDefault() - let el = event.currentTarget - let n = el.dataset.name - let t = el.innerText - GURPS.whisperOtfToOwner(t + ' ' + n, null, event, false, this.actor) - } - - async _onRightClickOtf(event) { - event.preventDefault() - let el = event.currentTarget - let isDamageRoll = el.dataset.hasOwnProperty('damage') - let otf = event.currentTarget.dataset.otf - - if (isDamageRoll) { - GURPS.resolveDamageRoll(event, this.actor, otf, null, game.user.isGM) - } else { - GURPS.whisperOtfToOwner(event.currentTarget.dataset.otf, null, event, !isDamageRoll, this.actor) // Can't blind roll damages (yet) - } - } - - async _onClickRoll(event, targets) { - GURPS.handleRoll(event, this.actor, { targets: targets }) - } - - async _onClickSplit(event) { - let element = event.currentTarget - let key = element.dataset.key - new SplitDREditor(this.actor, key).render(true) - } - - async _onNavigate(event) { - let dataValue = $(event.currentTarget).attr('data-value') - let windowContent = event.currentTarget.closest('.window-content') - let target = windowContent.querySelector(`#${dataValue}`) - - // The '33' represents the height of the window title bar + a bit of margin - // TODO: we should really look this up and use the actual values as found in the DOM. - windowContent.scrollTop = target.offsetTop - 33 - - // add the glowing class to target AND to event.currentTarget, then remove it - $(target).addClass('glowing') - $(event.currentTarget).addClass('glowing') - - setTimeout(function() { - $(target).removeClass('glowing') - $(event.currentTarget).removeClass('glowing') - }, 2000) - } - - async _onClickEnc(ev) { - ev.preventDefault() - if (!game.settings.get(settings.SYSTEM_NAME, settings.SETTING_AUTOMATIC_ENCUMBRANCE)) { - let element = ev.currentTarget - let key = element.dataset.key - ////////// - // Check for 'undefined' when clicking on Encumbrance Level 'header'. ~Stevil - if (key !== undefined) { - ////////// - let encs = this.actor.system.encumbrance - if (encs[key].current) return // already selected - for (let enckey in encs) { - let enc = encs[enckey] - let t = 'data.encumbrance.' + enckey + '.current' - if (key === enckey) { - await this.actor.update({ - [t]: true, - 'data.currentmove': parseInt(enc.move), - 'data.currentdodge': parseInt(enc.dodge), - }) - } else if (enc.current) { - await this.actor.update({ [t]: false }) - } - } - ////////// - } - ////////// - } else { - ui.notifications.warn( - "You cannot manually change the Encumbrance level. The 'Automatically calculate Encumbrance Level' setting is turned on." - ) - } - } - - async _onClickEquip(ev) { - ev.preventDefault() - let element = ev.currentTarget - let key = element.dataset.key - let eqt = duplicate(GURPS.decode(this.actor, key)) - eqt.equipped = !eqt.equipped - await this.actor.update({ [key]: eqt }) - await this.actor.updateItemAdditionsBasedOn(eqt, key) - let p = this.actor.getEquippedParry() - let b = this.actor.getEquippedBlock() - await this.actor.update({ - 'data.equippedparry': p, - 'data.equippedblock': b, - }) - this.actor._forceRender() - } - - deleteItemMenu(obj) { - return [ - { - name: 'Delete', - icon: "", - callback: e => { - let key = e[0].dataset.key - if (key.includes('.equipment.')) this.actor.deleteEquipment(key) - else GURPS.removeKey(this.actor, key) - }, - }, - ] - } - - /* -------------------------------------------- */ - - /** @override */ - _updateObject(event, formData) { - return super._updateObject(event, formData) - } + buttons: { + save: { + icon: '', + label: 'Save', + callback: html => { + const i = html[0].querySelector('#i') + actor.update({ 'data.additionalresources.qnotes': i.value.replace(/\n/g, '${i18n('GURPS.dropResolve')}
`, + buttons: { + one: { + icon: '', + label: `${i18n('GURPS.dropBefore')}`, + callback: async () => { + if (!isSrcFirst) { + await this._removeKey(sourceKey) + await this._insertBeforeKey(targetkey, object) + } else { + await this._insertBeforeKey(targetkey, object) + await this._removeKey(sourceKey) + } + }, + }, + two: { + icon: '', + label: `${i18n('GURPS.dropInside')}`, + callback: async () => { + let key = targetkey + '.contains.' + zeroFill(0) + if (!isSrcFirst) { + await this._removeKey(sourceKey) + await this._insertBeforeKey(key, object) + } else { + await this._insertBeforeKey(key, object) + await this._removeKey(sourceKey) + } + }, + }, + }, + default: 'one', + }) + d.render(true) + } + } + } + + async _insertBeforeKey(targetKey, element) { + // target key is the whole path, like 'data.melee.00001' + let components = targetKey.split('.') + + let index = parseInt(components.pop()) + let path = components.join('.') + + let object = GURPS.decode(this.actor, path) + let array = objectToArray(object) + + // Delete the whole object. + let last = components.pop() + let t = `${components.join('.')}.-=${last}` + await this.actor.internalUpdate({ [t]: null }) + + // Insert the element into the array. + array.splice(index, 0, element) + + // Convert back to an object + object = arrayToObject(array, 5) + + // update the actor + await this.actor.internalUpdate({ [path]: object }, { diff: false }) + } + + async _removeKey(sourceKey) { + // source key is the whole path, like 'data.melee.00001' + let components = sourceKey.split('.') + + let index = parseInt(components.pop()) + let path = components.join('.') + + let object = GURPS.decode(this.actor, path) + let array = objectToArray(object) + + // Delete the whole object. + let last = components.pop() + let t = `${components.join('.')}.-=${last}` + await this.actor.internalUpdate({ [t]: null }) + + // Remove the element from the array + array.splice(index, 1) + + // Convert back to an object + object = arrayToObject(array, 5) + + // update the actor + await this.actor.internalUpdate({ [path]: object }, { diff: false }) + } + + _onfocus(ev) { + ev.preventDefault() + GURPS.SetLastActor(this.actor) + } + + /** @override */ + setPosition(options = {}) { + const position = super.setPosition(options) + const sheetBody = this.element.find('.sheet-body') + if (!!position.height) { + const bodyHeight = position.height - 192 + sheetBody.css('height', bodyHeight) + } + return position + } + + get title() { + const t = this.actor.name + const sheet = this.actor.getFlag('core', 'sheetClass') + return sheet === 'gurps.GurpsActorEditorSheet' ? '**** Editing: ' + t + ' ****' : t + } + + _getHeaderButtons() { + let buttons = super._getHeaderButtons() + + // Token Configuration + if (this.options.editable && isConfigurationAllowed(this.actor)) { + buttons = this.getCustomHeaderButtons().concat(buttons) + } + return buttons + } + + /** + * Override this to change the buttons appended to the actor sheet title bar. + */ + getCustomHeaderButtons() { + const sheet = this.actor.getFlag('core', 'sheetClass') + const isEditor = sheet === 'gurps.GurpsActorEditorSheet' + const altsheet = game.settings.get(settings.SYSTEM_NAME, settings.SETTING_ALT_SHEET) + + const isFull = sheet === undefined || sheet === 'gurps.GurpsActorSheet' + let b = [ + { + label: isFull ? altsheet : 'Full View', + class: 'toggle', + icon: 'fas fa-exchange-alt', + onclick: ev => this._onToggleSheet(ev, altsheet), + }, + ] + + if (!game.settings.get(settings.SYSTEM_NAME, settings.SETTING_BLOCK_IMPORT) || game.user.isTrusted) + b.push({ + label: 'Import', + class: 'import', + icon: 'fas fa-file-import', + onclick: ev => this._onFileImport(ev), + }) + + if (!isEditor) { + b.push({ + label: 'Editor', + class: 'edit', + icon: 'fas fa-edit', + onclick: ev => this._onOpenEditor(ev), + }) + } + return b + } + + async _onFileImport(event) { + event.preventDefault() + this.actor.importCharacter() + } + + async _onToggleSheet(event, altsheet) { + event.preventDefault() + let newSheet = Object.values(CONFIG.Actor.sheetClasses['character']).filter(s => s.label == altsheet)[0].id + + const original = + this.actor.getFlag('core', 'sheetClass') || + Object.values(CONFIG.Actor.sheetClasses['character']).filter(s => s.default)[0].id + console.log('original: ' + original) + + if (original != 'gurps.GurpsActorSheet') newSheet = 'gurps.GurpsActorSheet' + if (event.shiftKey) + // Hold down the shift key for Simplified + newSheet = 'gurps.GurpsActorSimplifiedSheet' + if (game.keyboard.isModifierActive(KeyboardManager.MODIFIER_KEYS.CONTROL)) + // Hold down the Ctrl key (Command on Mac) for Simplified + newSheet = 'gurps.GurpsActorNpcSheet' + + this.actor.openSheet(newSheet) + } + + async _onOpenEditor(event) { + event.preventDefault() + this.actor.openSheet('gurps.GurpsActorEditorSheet') + } + + async _onRightClickGurpslink(event) { + event.preventDefault() + event.stopImmediatePropagation() // Since this may occur in note or a list (which has its own RMB handler) + let el = event.currentTarget + let action = el.dataset.action + if (!!action) { + action = JSON.parse(atou(action)) + if (action.type === 'damage' || action.type === 'deriveddamage') { + GURPS.resolveDamageRoll(event, this.actor, action.orig, action.overridetxt, game.user.isGM, true) + } else { + GURPS.whisperOtfToOwner(action.orig, action.overridetxt, event, action, this.actor) // only offer blind rolls for things that can be blind, No need to offer blind roll if it is already blind + } + } + } + + async _onRightClickPdf(event) { + event.preventDefault() + let el = event.currentTarget + GURPS.whisperOtfToOwner('PDF:' + el.innerText, null, event, false, this.actor) + } + + async _onRightClickGmod(event) { + event.preventDefault() + let el = event.currentTarget + let n = el.dataset.name + let t = el.innerText + GURPS.whisperOtfToOwner(t + ' ' + n, null, event, false, this.actor) + } + + async _onRightClickOtf(event) { + event.preventDefault() + let el = event.currentTarget + let isDamageRoll = el.dataset.hasOwnProperty('damage') + let otf = event.currentTarget.dataset.otf + + if (isDamageRoll) { + GURPS.resolveDamageRoll(event, this.actor, otf, null, game.user.isGM) + } else { + GURPS.whisperOtfToOwner(event.currentTarget.dataset.otf, null, event, !isDamageRoll, this.actor) // Can't blind roll damages (yet) + } + } + + async _onClickRoll(event, targets) { + GURPS.handleRoll(event, this.actor, { targets: targets }) + } + + async _onClickSplit(event) { + let element = event.currentTarget + let key = element.dataset.key + new SplitDREditor(this.actor, key).render(true) + } + + async _onNavigate(event) { + let dataValue = $(event.currentTarget).attr('data-value') + let windowContent = event.currentTarget.closest('.window-content') + let target = windowContent.querySelector(`#${dataValue}`) + + // The '33' represents the height of the window title bar + a bit of margin + // TODO: we should really look this up and use the actual values as found in the DOM. + windowContent.scrollTop = target.offsetTop - 33 + + // add the glowing class to target AND to event.currentTarget, then remove it + $(target).addClass('glowing') + $(event.currentTarget).addClass('glowing') + + setTimeout(function () { + $(target).removeClass('glowing') + $(event.currentTarget).removeClass('glowing') + }, 2000) + } + + async _onClickEnc(ev) { + ev.preventDefault() + if (!game.settings.get(settings.SYSTEM_NAME, settings.SETTING_AUTOMATIC_ENCUMBRANCE)) { + let element = ev.currentTarget + let key = element.dataset.key + ////////// + // Check for 'undefined' when clicking on Encumbrance Level 'header'. ~Stevil + if (key !== undefined) { + ////////// + let encs = this.actor.system.encumbrance + if (encs[key].current) return // already selected + for (let enckey in encs) { + let enc = encs[enckey] + let t = 'data.encumbrance.' + enckey + '.current' + if (key === enckey) { + await this.actor.update({ + [t]: true, + 'data.currentmove': parseInt(enc.move), + 'data.currentdodge': parseInt(enc.dodge), + }) + } else if (enc.current) { + await this.actor.update({ [t]: false }) + } + } + ////////// + } + ////////// + } else { + ui.notifications.warn( + "You cannot manually change the Encumbrance level. The 'Automatically calculate Encumbrance Level' setting is turned on." + ) + } + } + + async _onClickEquip(ev) { + ev.preventDefault() + let element = ev.currentTarget + let key = element.dataset.key + let eqt = duplicate(GURPS.decode(this.actor, key)) + eqt.equipped = !eqt.equipped + await this.actor.update({ [key]: eqt }) + await this.actor.updateItemAdditionsBasedOn(eqt, key) + let p = this.actor.getEquippedParry() + let b = this.actor.getEquippedBlock() + await this.actor.update({ + 'data.equippedparry': p, + 'data.equippedblock': b, + }) + this.actor._forceRender() + } + + deleteItemMenu(obj) { + return [ + { + name: 'Delete', + icon: "", + callback: e => { + let key = e[0].dataset.key + if (key.includes('.equipment.')) this.actor.deleteEquipment(key) + else GURPS.removeKey(this.actor, key) + }, + }, + ] + } + + /* -------------------------------------------- */ + + /** @override */ + _updateObject(event, formData) { + return super._updateObject(event, formData) + } } export class GurpsActorTabSheet extends GurpsActorSheet { - /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { - classes: ['gurps', 'sheet', 'actor'], - width: 860, - height: 600, - tabs: [{ navSelector: '.gurps-sheet-tabs', contentSelector: '.sheet-body', initial: 'description' }], - dragDrop: [{ dragSelector: '.item-list .item', dropSelector: null }], - }) - } - - /* -------------------------------------------- */ - - /** @override */ - get template() { - if (!game.user.isGM && this.actor.limited) return 'systems/gurps/templates/actor/actor-sheet-gcs-limited.hbs' - return 'systems/gurps/templates/actor/actor-tab-sheet.hbs' - } + /** @override */ + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ['gurps', 'sheet', 'actor'], + width: 860, + height: 600, + tabs: [{ navSelector: '.gurps-sheet-tabs', contentSelector: '.sheet-body', initial: 'description' }], + dragDrop: [{ dragSelector: '.item-list .item', dropSelector: null }], + }) + } + + /* -------------------------------------------- */ + + /** @override */ + get template() { + if (!game.user.isGM && this.actor.limited) return 'systems/gurps/templates/actor/actor-sheet-gcs-limited.hbs' + return 'systems/gurps/templates/actor/actor-tab-sheet.hbs' + } } export class GurpsActorCombatSheet extends GurpsActorSheet { - /** @override */ - static get defaultOptions() { - return mergeObject(super.defaultOptions, { - classes: ['gurps', 'sheet', 'actor'], - width: 670, - height: 'auto', - tabs: [{ navSelector: '.gurps-sheet-tabs', contentSelector: '.sheet-body', initial: 'description' }], - dragDrop: [{ dragSelector: '.item-list .item', dropSelector: null }], - }) - } - - /* -------------------------------------------- */ - - /** @override */ - get template() { - if (!game.user.isGM && this.actor.limited) return 'systems/gurps/templates/actor/actor-sheet-gcs-limited.hbs' - return 'systems/gurps/templates/actor/combat-sheet.hbs' - } + /** @override */ + static get defaultOptions() { + return mergeObject(super.defaultOptions, { + classes: ['gurps', 'sheet', 'actor'], + width: 670, + height: 'auto', + tabs: [{ navSelector: '.gurps-sheet-tabs', contentSelector: '.sheet-body', initial: 'description' }], + dragDrop: [{ dragSelector: '.item-list .item', dropSelector: null }], + }) + } + + /* -------------------------------------------- */ + + /** @override */ + get template() { + if (!game.user.isGM && this.actor.limited) return 'systems/gurps/templates/actor/actor-sheet-gcs-limited.hbs' + return 'systems/gurps/templates/actor/combat-sheet.hbs' + } } Hooks.on('getGurpsActorEditorSheetHeaderButtons', sheet => { - if (sheet.actor.isEmptyActor()) { - ui.notifications.error('You are editing an EMPTY Actor!') - setTimeout( - () => - Dialog.prompt({ - title: 'Empty Actor', - content: - 'You are editing an EMPTY Actor!and replace
with newlines from "formatted text" - let origx = GURPS.cleanUpP(xml) - let x = xmlTextToJson(origx) - // @ts-ignore - let r = x.root - let msg = [] - let version = 'unknown' - let vernum = 1 - let exit = false - if (!r) { - if (importname.endsWith('.gca5')) msg.push(i18n('GURPS.importCannotImportGCADirectly')) - if (importname.endsWith('.gca4')) msg.push(i18n('GURPS.importCannotImportGCADirectly')) - else if (!xml.startsWith(' 0) { - ui.notifications?.error(msg.join('Where do you want to place this?
', - buttons: { - one: { - icon: '', - label: 'Before', - callback: async () => { - this.ignoreRender = true - if (!isSrcFirst) { - await GURPS.removeKey(this, srckey) - await this.updateParentOf(srckey, false) - } - await this.updateItemAdditionsBasedOn(object, targetkey) - await GURPS.insertBeforeKey(this, targetkey, object) - await this.updateParentOf(targetkey, true) - if (isSrcFirst) { - await GURPS.removeKey(this, srckey) - await this.updateParentOf(srckey, false) - } - this._forceRender() - }, - }, - two: { - icon: '', - label: 'In', - callback: async () => { - this.ignoreRender = true - if (!isSrcFirst) { - await GURPS.removeKey(this, srckey) - await this.updateParentOf(srckey, false) - } - let k = targetkey + '.contains.' + zeroFill(0) - let targ = getProperty(this, targetkey) - - await this.updateItemAdditionsBasedOn(object, targetkey) - await GURPS.insertBeforeKey(this, k, object) - await this.updateParentOf(k, true) - if (isSrcFirst) { - await GURPS.removeKey(this, srckey) - await this.updateParentOf(srckey, false) - } - this._forceRender() - }, - }, - }, - default: 'one', - }) - d.render(true) - } - - /** - * @param {string} path - */ - async toggleExpand(path, expandOnly = false) { - let obj = getProperty(this, path) - if (!!obj.collapsed && Object.keys(obj.collapsed).length > 0) { - let temp = { ...obj.contains, ...obj.collapsed } - let update = { - [path + '.-=collapsed']: null, - [path + '.collapsed']: {}, - [path + '.contains']: temp, - } - await this.update(update) - } else if (!expandOnly && !!obj.contains && Object.keys(obj.contains).length > 0) { - let temp = { ...obj.contains, ...obj.collapsed } - let update = { - [path + '.-=contains']: null, - [path + '.contains']: {}, - [path + '.collapsed']: temp, - } - await this.update(update) - } - } - - /** - * @param {string} srckey - * @param {string} targetkey - */ - async _splitEquipment(srckey, targetkey) { - let srceqt = getProperty(this, srckey) - if (srceqt.count <= 1) return false // nothing to split - let content = await renderTemplate('systems/gurps/templates/transfer-equipment.html', { eqt: srceqt }) - let count = 0 - let callback = async (/** @type {JQueryand replace
with newlines from "formatted text" + let origx = GURPS.cleanUpP(xml) + let x = xmlTextToJson(origx) + // @ts-ignore + let r = x.root + let msg = [] + let version = 'unknown' + let vernum = 1 + let exit = false + if (!r) { + if (importname.endsWith('.gca5')) msg.push(i18n('GURPS.importCannotImportGCADirectly')) + if (importname.endsWith('.gca4')) msg.push(i18n('GURPS.importCannotImportGCADirectly')) + else if (!xml.startsWith(' 0) { + ui.notifications?.error(msg.join('Where do you want to place this?
', + buttons: { + one: { + icon: '', + label: 'Before', + callback: async () => { + this.ignoreRender = true + if (!isSrcFirst) { + await GURPS.removeKey(this, srckey) + await this.updateParentOf(srckey, false) + } + await this.updateItemAdditionsBasedOn(object, targetkey) + await GURPS.insertBeforeKey(this, targetkey, object) + await this.updateParentOf(targetkey, true) + if (isSrcFirst) { + await GURPS.removeKey(this, srckey) + await this.updateParentOf(srckey, false) + } + this._forceRender() + }, + }, + two: { + icon: '', + label: 'In', + callback: async () => { + this.ignoreRender = true + if (!isSrcFirst) { + await GURPS.removeKey(this, srckey) + await this.updateParentOf(srckey, false) + } + let k = targetkey + '.contains.' + zeroFill(0) + let targ = getProperty(this, targetkey) + + await this.updateItemAdditionsBasedOn(object, targetkey) + await GURPS.insertBeforeKey(this, k, object) + await this.updateParentOf(k, true) + if (isSrcFirst) { + await GURPS.removeKey(this, srckey) + await this.updateParentOf(srckey, false) + } + this._forceRender() + }, + }, + }, + default: 'one', + }) + d.render(true) + } + + /** + * @param {string} path + */ + async toggleExpand(path, expandOnly = false) { + let obj = getProperty(this, path) + if (!!obj.collapsed && Object.keys(obj.collapsed).length > 0) { + let temp = { ...obj.contains, ...obj.collapsed } + let update = { + [path + '.-=collapsed']: null, + [path + '.collapsed']: {}, + [path + '.contains']: temp, + } + await this.update(update) + } else if (!expandOnly && !!obj.contains && Object.keys(obj.contains).length > 0) { + let temp = { ...obj.contains, ...obj.collapsed } + let update = { + [path + '.-=contains']: null, + [path + '.contains']: {}, + [path + '.collapsed']: temp, + } + await this.update(update) + } + } + + /** + * @param {string} srckey + * @param {string} targetkey + */ + async _splitEquipment(srckey, targetkey) { + let srceqt = getProperty(this, srckey) + if (srceqt.count <= 1) return false // nothing to split + let content = await renderTemplate('systems/gurps/templates/transfer-equipment.html', { eqt: srceqt }) + let count = 0 + let callback = async (/** @type {JQuery