123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148 |
- // Enclose abbreviations in <abbr> tags
- //
- 'use strict';
- module.exports = function sub_plugin(md) {
- var escapeRE = md.utils.escapeRE,
- arrayReplaceAt = md.utils.arrayReplaceAt;
- // ASCII characters in Cc, Sc, Sm, Sk categories we should terminate on;
- // you can check character classes here:
- // http://www.unicode.org/Public/UNIDATA/UnicodeData.txt
- var OTHER_CHARS = ' \r\n$+<=>^`|~';
- var UNICODE_PUNCT_RE = md.utils.lib.ucmicro.P.source;
- var UNICODE_SPACE_RE = md.utils.lib.ucmicro.Z.source;
- function abbr_def(state, startLine, endLine, silent) {
- var label, title, ch, labelStart, labelEnd,
- pos = state.bMarks[startLine] + state.tShift[startLine],
- max = state.eMarks[startLine];
- if (pos + 2 >= max) { return false; }
- if (state.src.charCodeAt(pos++) !== 0x2A/* * */) { return false; }
- if (state.src.charCodeAt(pos++) !== 0x5B/* [ */) { return false; }
- labelStart = pos;
- for (; pos < max; pos++) {
- ch = state.src.charCodeAt(pos);
- if (ch === 0x5B /* [ */) {
- return false;
- } else if (ch === 0x5D /* ] */) {
- labelEnd = pos;
- break;
- } else if (ch === 0x5C /* \ */) {
- pos++;
- }
- }
- if (labelEnd < 0 || state.src.charCodeAt(labelEnd + 1) !== 0x3A/* : */) {
- return false;
- }
- if (silent) { return true; }
- label = state.src.slice(labelStart, labelEnd).replace(/\\(.)/g, '$1');
- title = state.src.slice(labelEnd + 2, max).trim();
- if (label.length === 0) { return false; }
- if (title.length === 0) { return false; }
- if (!state.env.abbreviations) { state.env.abbreviations = {}; }
- // prepend ':' to avoid conflict with Object.prototype members
- if (typeof state.env.abbreviations[':' + label] === 'undefined') {
- state.env.abbreviations[':' + label] = title;
- }
- state.line = startLine + 1;
- return true;
- }
- function abbr_replace(state) {
- var i, j, l, tokens, token, text, nodes, pos, reg, m, regText, regSimple,
- currentToken,
- blockTokens = state.tokens;
- if (!state.env.abbreviations) { return; }
- regSimple = new RegExp('(?:' +
- Object.keys(state.env.abbreviations).map(function (x) {
- return x.substr(1);
- }).sort(function (a, b) {
- return b.length - a.length;
- }).map(escapeRE).join('|') +
- ')');
- regText = '(^|' + UNICODE_PUNCT_RE + '|' + UNICODE_SPACE_RE +
- '|[' + OTHER_CHARS.split('').map(escapeRE).join('') + '])'
- + '(' + Object.keys(state.env.abbreviations).map(function (x) {
- return x.substr(1);
- }).sort(function (a, b) {
- return b.length - a.length;
- }).map(escapeRE).join('|') + ')'
- + '($|' + UNICODE_PUNCT_RE + '|' + UNICODE_SPACE_RE +
- '|[' + OTHER_CHARS.split('').map(escapeRE).join('') + '])';
- reg = new RegExp(regText, 'g');
- for (j = 0, l = blockTokens.length; j < l; j++) {
- if (blockTokens[j].type !== 'inline') { continue; }
- tokens = blockTokens[j].children;
- // We scan from the end, to keep position when new tags added.
- for (i = tokens.length - 1; i >= 0; i--) {
- currentToken = tokens[i];
- if (currentToken.type !== 'text') { continue; }
- pos = 0;
- text = currentToken.content;
- reg.lastIndex = 0;
- nodes = [];
- // fast regexp run to determine whether there are any abbreviated words
- // in the current token
- if (!regSimple.test(text)) { continue; }
- while ((m = reg.exec(text))) {
- if (m.index > 0 || m[1].length > 0) {
- token = new state.Token('text', '', 0);
- token.content = text.slice(pos, m.index + m[1].length);
- nodes.push(token);
- }
- token = new state.Token('abbr_open', 'abbr', 1);
- token.attrs = [ [ 'title', state.env.abbreviations[':' + m[2]] ] ];
- nodes.push(token);
- token = new state.Token('text', '', 0);
- token.content = m[2];
- nodes.push(token);
- token = new state.Token('abbr_close', 'abbr', -1);
- nodes.push(token);
- reg.lastIndex -= m[3].length;
- pos = reg.lastIndex;
- }
- if (!nodes.length) { continue; }
- if (pos < text.length) {
- token = new state.Token('text', '', 0);
- token.content = text.slice(pos);
- nodes.push(token);
- }
- // replace current node
- blockTokens[j].children = tokens = arrayReplaceAt(tokens, i, nodes);
- }
- }
- }
- md.block.ruler.before('reference', 'abbr_def', abbr_def, { alt: [ 'paragraph', 'reference' ] });
- md.core.ruler.after('linkify', 'abbr_replace', abbr_replace);
- };
|