diff --git a/lib/rules_core/typographer.js b/lib/rules_core/typographer.js index 54c8f4a..4f2eda6 100644 --- a/lib/rules_core/typographer.js +++ b/lib/rules_core/typographer.js @@ -2,13 +2,6 @@ module.exports = function typographer(state) { if (!state.options.typographer) { return; } - 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.typographer.process(tok, state); - } - } + state.typographer.process(state); }; diff --git a/lib/rules_text/replace.js b/lib/rules_text/replace.js index 88d5c39..9d76b61 100644 --- a/lib/rules_text/replace.js +++ b/lib/rules_text/replace.js @@ -6,54 +6,61 @@ var COPY_RE = /\((c|tm|r|p)\)/i; var RARE_RE = /\+-|\.\.|\?\?\?\?|!!!!|,,|--/; -module.exports = function replace(typographer, blockToken) { - var i, token, text, - tokens = blockToken.children, +module.exports = function replace(state) { + var i, token, text, inlineTokens, blkIdx, + typographer = state.typographer, options = typographer.options; - for (i = tokens.length - 1; i >= 0; i--) { - token = tokens[i]; - if (token.type === 'text') { - text = token.content; + for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { - if (COPY_RE.test(text)) { - if (options.copyright) { - text = text.replace(/\(c\)/gi, '©'); - } - if (options.trademark) { - text = text.replace(/\(tm\)/gi, '™'); - } - if (options.registered) { - text = text.replace(/\(r\)/gi, '®'); - } - if (options.paragraph) { - text = text.replace(/\(p\)/gi, '§'); - } - } + if (state.tokens[blkIdx].type !== 'inline') { continue; } - if (RARE_RE.test(text)) { - if (options.plusminus) { - text = text.replace(/\+-/g, '±'); - } - if (options.ellipsis) { - // .., ..., ....... -> … - // but ?..... & !..... -> ?.. & !.. - text = text.replace(/\.{2,}/g, '…').replace(/([?!])…/g, '$1..'); - } - if (options.dupes) { - text = text.replace(/([?!]){4,}/g, '$1$1$1').replace(/,{2,}/g, ','); + inlineTokens = state.tokens[blkIdx].children; + + for (i = inlineTokens.length - 1; i >= 0; i--) { + token = inlineTokens[i]; + if (token.type === 'text') { + text = token.content; + + if (COPY_RE.test(text)) { + if (options.copyright) { + text = text.replace(/\(c\)/gi, '©'); + } + if (options.trademark) { + text = text.replace(/\(tm\)/gi, '™'); + } + if (options.registered) { + text = text.replace(/\(r\)/gi, '®'); + } + if (options.paragraph) { + text = text.replace(/\(p\)/gi, '§'); + } } - if (options.dashes) { - text = text - // em-dash - .replace(/(^|[^-])---([^-]|$)/mg, '$1\u2014$2') - // en-dash - .replace(/(^|\s)--(\s|$)/mg, '$1\u2013$2') - .replace(/(^|[^-\s])--([^-\s]|$)/mg, '$1\u2013$2'); + + if (RARE_RE.test(text)) { + if (options.plusminus) { + text = text.replace(/\+-/g, '±'); + } + if (options.ellipsis) { + // .., ..., ....... -> … + // but ?..... & !..... -> ?.. & !.. + text = text.replace(/\.{2,}/g, '…').replace(/([?!])…/g, '$1..'); + } + if (options.dupes) { + text = text.replace(/([?!]){4,}/g, '$1$1$1').replace(/,{2,}/g, ','); + } + if (options.dashes) { + text = text + // 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; + token.content = text; + } } } }; diff --git a/lib/rules_text/smartquotes.js b/lib/rules_text/smartquotes.js index 8febc38..fe495ec 100644 --- a/lib/rules_text/smartquotes.js +++ b/lib/rules_text/smartquotes.js @@ -20,82 +20,90 @@ function replaceAt(str, index, ch) { return str.substr(0, index) + ch + str.substr(index + 1); } -var stack = []; -module.exports = function smartquotes(typographer, blockToken) { +module.exports = function smartquotes(state) { /*eslint max-depth:0*/ - var i, token, text, t, pos, max, thisLevel, lastSpace, nextSpace, item, canOpen, canClose, j, isSingle, chars, + var i, token, text, t, pos, max, thisLevel, lastSpace, nextSpace, item, + canOpen, canClose, j, isSingle, chars, blkIdx, tokens, + typographer = state.typographer, options = typographer.options, - tokens = blockToken.children; + stack = []; - stack.length = 0; - for (i = 0; i < tokens.length; i++) { - token = tokens[i]; + for (blkIdx = state.tokens.length - 1; blkIdx >= 0; blkIdx--) { - if (token.type !== 'text' || QUOTE_TEST_RE.test(token.text)) { continue; } + if (state.tokens[blkIdx].type !== 'inline') { continue; } - thisLevel = tokens[i].level; + tokens = state.tokens[blkIdx].children; + stack.length = 0; - 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; } - - lastSpace = !isLetter(text, t.index - 1); - pos = t.index + 1; - isSingle = (t[0] === "'"); - nextSpace = !isLetter(text, pos); - - if (!nextSpace && !lastSpace) { - // middle of word - if (isSingle) { - token.content = replaceAt(token.content, t.index, APOSTROPHE); - } - continue; + 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; } + + lastSpace = !isLetter(text, t.index - 1); + pos = t.index + 1; + isSingle = (t[0] === "'"); + nextSpace = !isLetter(text, pos); + + if (!nextSpace && !lastSpace) { + // middle of word + if (isSingle) { + token.content = replaceAt(token.content, t.index, APOSTROPHE); + } + continue; + } - canOpen = !nextSpace; - canClose = !lastSpace; + canOpen = !nextSpace; + canClose = !lastSpace; - 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) { + 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]; - chars = isSingle ? options.singleQuotes : options.doubleQuotes; - if (chars) { - tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, chars[0]); - token.content = replaceAt(token.content, t.index, chars[1]); + if (stack[j].level < thisLevel) { break; } + if (item.single === isSingle && stack[j].level === thisLevel) { + item = stack[j]; + chars = isSingle ? options.singleQuotes : options.doubleQuotes; + if (chars) { + tokens[item.token].content = replaceAt(tokens[item.token].content, item.pos, chars[0]); + token.content = replaceAt(token.content, t.index, chars[1]); + } + stack.length = j; + continue OUTER; } - 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); + 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); + } } } } diff --git a/lib/typographer.js b/lib/typographer.js index dd549e1..1d30d02 100644 --- a/lib/typographer.js +++ b/lib/typographer.js @@ -33,13 +33,13 @@ Typographer.prototype.set = function (options) { }; -Typographer.prototype.process = function (token) { +Typographer.prototype.process = function (state) { var i, l, rules; rules = this.ruler.getRules(''); for (i = 0, l = rules.length; i < l; i++) { - rules[i](this, token); + rules[i](state); } };