Browse Source

Moved scans from typorgapher directly to rules

pull/14/head
Vitaly Puzrin 10 years ago
parent
commit
8294a63f8c
  1. 9
      lib/rules_core/typographer.js
  2. 89
      lib/rules_text/replace.js
  3. 128
      lib/rules_text/smartquotes.js
  4. 4
      lib/typographer.js

9
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);
};

89
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;
}
}
}
};

128
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);
}
}
}
}

4
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);
}
};

Loading…
Cancel
Save