@retorquere/bibtex-parser 8.0.12 → 8.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/index.js +9 -9
- package/package.json +10 -9
- package/prepositions.json +88 -0
- package/sentence-case.d.ts +1 -25
- package/sentence-case.js +40 -134
package/index.js
CHANGED
|
@@ -57,6 +57,7 @@ const JabRef = __importStar(require("./jabref"));
|
|
|
57
57
|
var sentence_case_1 = require("./sentence-case");
|
|
58
58
|
Object.defineProperty(exports, "toSentenceCase", { enumerable: true, get: function () { return sentence_case_1.toSentenceCase; } });
|
|
59
59
|
const sentence_case_2 = require("./sentence-case");
|
|
60
|
+
const tokenizer_1 = require("./tokenizer");
|
|
60
61
|
const crossref_json_1 = __importDefault(require("./crossref.json"));
|
|
61
62
|
const fields_json_1 = __importDefault(require("./fields.json"));
|
|
62
63
|
const unabbreviations = require('./unabbrev.json');
|
|
@@ -753,14 +754,13 @@ class BibTeXParser {
|
|
|
753
754
|
if (FieldAction.parseInt.includes(field) && value.trim().match(/^-?\d+$/))
|
|
754
755
|
return `${parseInt(value)}`;
|
|
755
756
|
if (mode === 'title' && sentenceCase) {
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
}
|
|
757
|
+
value = (0, sentence_case_2.toSentenceCase)(value, {
|
|
758
|
+
preserveQuoted: this.options.sentenceCase.preserveQuoted,
|
|
759
|
+
subSentenceCapitalization: this.options.sentenceCase.subSentence,
|
|
760
|
+
markup: /\x0E\/?([a-z]+)\x0F/ig,
|
|
761
|
+
nocase: /\x0E(ncx?)\x0F.*?\x0E\/\1\x0F/ig,
|
|
762
|
+
guess: this.options.sentenceCase.guess && !value.match(/\x0E(nc)\x0F/),
|
|
763
|
+
});
|
|
764
764
|
let cancel = (_match, stripped) => stripped;
|
|
765
765
|
switch (this.options.caseProtection) {
|
|
766
766
|
case 'strict':
|
|
@@ -768,7 +768,7 @@ class BibTeXParser {
|
|
|
768
768
|
break;
|
|
769
769
|
case 'as-needed':
|
|
770
770
|
cancel = (match, stripped) => {
|
|
771
|
-
const words = (0,
|
|
771
|
+
const words = (0, tokenizer_1.tokenize)(stripped, /\x0E\/?([a-z]+)\x0F/ig);
|
|
772
772
|
return words.find(w => w.shape.match(/^(?!.*X).*x.*$/)) ? match : this.wrap(stripped, 'ncx');
|
|
773
773
|
};
|
|
774
774
|
break;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@retorquere/bibtex-parser",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.0.13",
|
|
4
4
|
"description": "BibTeX parser",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"typings": "./index.d.ts",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"update-cases": "curl -s https://raw.githubusercontent.com/retorquere/zotero-better-bibtex/master/test/loaded.json | jq -r '.[] | select(test(\"\\\\.bib((la)?tex)?$\"))' | rsync -av --delete -m --files-from=- ../better-bibtex/test/fixtures/ test/better-bibtex/",
|
|
24
24
|
"predocs": "npm start",
|
|
25
25
|
"docs": "typedoc",
|
|
26
|
-
"ncu": "./build/submodules.py && ./build/crossref.py && ncu -u && npm i && git add package.json package-lock.json && npm audit fix && git commit -m ncu"
|
|
26
|
+
"ncu": "./build/submodules.py && ./build/crossref.py && ncu -u -x eslint && npm i && git add package.json package-lock.json && npm audit fix && git commit -m ncu"
|
|
27
27
|
},
|
|
28
28
|
"repository": {
|
|
29
29
|
"type": "git",
|
|
@@ -35,9 +35,9 @@
|
|
|
35
35
|
"author": "Emiliano Heyns",
|
|
36
36
|
"license": "ISC",
|
|
37
37
|
"devDependencies": {
|
|
38
|
-
"@types/node": "^20.12.
|
|
39
|
-
"@typescript-eslint/eslint-plugin": "^7.
|
|
40
|
-
"@typescript-eslint/parser": "^7.
|
|
38
|
+
"@types/node": "^20.12.7",
|
|
39
|
+
"@typescript-eslint/eslint-plugin": "^7.6.0",
|
|
40
|
+
"@typescript-eslint/parser": "^7.6.0",
|
|
41
41
|
"command-line-args": "^5.2.1",
|
|
42
42
|
"csv": "^6.3.8",
|
|
43
43
|
"digital-tree": "^2.0.4",
|
|
@@ -54,15 +54,15 @@
|
|
|
54
54
|
"tap": "^18.7.2",
|
|
55
55
|
"ts-node": "^10.9.2",
|
|
56
56
|
"tslib": "^2.6.2",
|
|
57
|
-
"typedoc": "^0.25.
|
|
58
|
-
"typescript": "^5.4.
|
|
57
|
+
"typedoc": "^0.25.13",
|
|
58
|
+
"typescript": "^5.4.5"
|
|
59
59
|
},
|
|
60
60
|
"dependencies": {
|
|
61
61
|
"@unified-latex/unified-latex-util-pegjs": "^1.7.1",
|
|
62
62
|
"@unified-latex/unified-latex-util-print-raw": "^1.7.1",
|
|
63
63
|
"@unified-latex/unified-latex-util-replace": "^1.7.1",
|
|
64
64
|
"@unified-latex/unified-latex-util-visit": "^1.7.1",
|
|
65
|
-
"
|
|
65
|
+
"moo": "^0.5.2",
|
|
66
66
|
"unicode2latex": "^6.0.4",
|
|
67
67
|
"wink-eng-lite-web-model": "^1.6.0",
|
|
68
68
|
"wink-nlp": "^2.2.0",
|
|
@@ -84,7 +84,8 @@
|
|
|
84
84
|
"sentence-case.d.ts",
|
|
85
85
|
"fields.json",
|
|
86
86
|
"strings.bib",
|
|
87
|
-
"unabbrev.json"
|
|
87
|
+
"unabbrev.json",
|
|
88
|
+
"prepositions.json"
|
|
88
89
|
],
|
|
89
90
|
"publishConfig": {
|
|
90
91
|
"access": "public"
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
[
|
|
2
|
+
"a",
|
|
3
|
+
"about",
|
|
4
|
+
"above",
|
|
5
|
+
"according to",
|
|
6
|
+
"across",
|
|
7
|
+
"after",
|
|
8
|
+
"against",
|
|
9
|
+
"ahead of",
|
|
10
|
+
"along",
|
|
11
|
+
"although",
|
|
12
|
+
"among",
|
|
13
|
+
"an",
|
|
14
|
+
"and",
|
|
15
|
+
"apart from",
|
|
16
|
+
"around",
|
|
17
|
+
"as for",
|
|
18
|
+
"as if",
|
|
19
|
+
"as long as",
|
|
20
|
+
"as of",
|
|
21
|
+
"as per",
|
|
22
|
+
"as regards",
|
|
23
|
+
"as soon as",
|
|
24
|
+
"as",
|
|
25
|
+
"aside from",
|
|
26
|
+
"at",
|
|
27
|
+
"back to",
|
|
28
|
+
"because of",
|
|
29
|
+
"because",
|
|
30
|
+
"before",
|
|
31
|
+
"behind",
|
|
32
|
+
"below",
|
|
33
|
+
"beneath",
|
|
34
|
+
"beside",
|
|
35
|
+
"between",
|
|
36
|
+
"but",
|
|
37
|
+
"by",
|
|
38
|
+
"close to",
|
|
39
|
+
"down",
|
|
40
|
+
"due to",
|
|
41
|
+
"during",
|
|
42
|
+
"except for",
|
|
43
|
+
"far from",
|
|
44
|
+
"for",
|
|
45
|
+
"from",
|
|
46
|
+
"if",
|
|
47
|
+
"in",
|
|
48
|
+
"inside of",
|
|
49
|
+
"inside",
|
|
50
|
+
"instead of",
|
|
51
|
+
"into",
|
|
52
|
+
"like",
|
|
53
|
+
"near",
|
|
54
|
+
"next to",
|
|
55
|
+
"nor",
|
|
56
|
+
"of",
|
|
57
|
+
"off",
|
|
58
|
+
"on",
|
|
59
|
+
"onto",
|
|
60
|
+
"or",
|
|
61
|
+
"out",
|
|
62
|
+
"outside of",
|
|
63
|
+
"over",
|
|
64
|
+
"owing to",
|
|
65
|
+
"prior to",
|
|
66
|
+
"pursuant to",
|
|
67
|
+
"regardless of",
|
|
68
|
+
"right of",
|
|
69
|
+
"since",
|
|
70
|
+
"so",
|
|
71
|
+
"subsequent to",
|
|
72
|
+
"the",
|
|
73
|
+
"through",
|
|
74
|
+
"to",
|
|
75
|
+
"towards",
|
|
76
|
+
"under",
|
|
77
|
+
"underneath",
|
|
78
|
+
"unless",
|
|
79
|
+
"until",
|
|
80
|
+
"up",
|
|
81
|
+
"upon",
|
|
82
|
+
"when",
|
|
83
|
+
"while",
|
|
84
|
+
"with",
|
|
85
|
+
"within",
|
|
86
|
+
"without",
|
|
87
|
+
"yet"
|
|
88
|
+
]
|
package/sentence-case.d.ts
CHANGED
|
@@ -1,33 +1,9 @@
|
|
|
1
|
-
type WordToken = {
|
|
2
|
-
type: 'word';
|
|
3
|
-
text: string;
|
|
4
|
-
shape: string;
|
|
5
|
-
start: number;
|
|
6
|
-
end: number;
|
|
7
|
-
sentenceStart?: boolean;
|
|
8
|
-
subSentenceStart?: boolean;
|
|
9
|
-
};
|
|
10
|
-
type PunctuationToken = {
|
|
11
|
-
type: 'punctuation';
|
|
12
|
-
text: string;
|
|
13
|
-
shape: string;
|
|
14
|
-
start: number;
|
|
15
|
-
end: number;
|
|
16
|
-
};
|
|
17
|
-
type WhitespaceToken = {
|
|
18
|
-
type: 'whitespace';
|
|
19
|
-
text: string;
|
|
20
|
-
shape: string;
|
|
21
|
-
};
|
|
22
|
-
type Token = WordToken | PunctuationToken | WhitespaceToken;
|
|
23
|
-
export declare function tokenize(title: string, markup?: RegExp): Token[];
|
|
24
1
|
export type Options = {
|
|
25
2
|
preserveQuoted?: boolean;
|
|
26
3
|
subSentenceCapitalization?: boolean;
|
|
27
4
|
markup?: RegExp;
|
|
28
5
|
nocase?: RegExp;
|
|
6
|
+
guess?: boolean;
|
|
29
7
|
};
|
|
30
8
|
export declare function toSentenceCase(title: string, options?: Options): string;
|
|
31
|
-
export declare function guessSentenceCased(title: string, markup?: RegExp): boolean;
|
|
32
|
-
export {};
|
|
33
9
|
//# sourceMappingURL=sentence-case.d.ts.map
|
package/sentence-case.js
CHANGED
|
@@ -4,34 +4,17 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
exports.
|
|
7
|
+
exports.toSentenceCase = void 0;
|
|
8
8
|
const xregexp_1 = __importDefault(require("xregexp"));
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return {
|
|
13
|
-
start: tokens[0].start,
|
|
14
|
-
end: tokens[tokens.length - 1].end,
|
|
15
|
-
text: tokens.map(c => c.text).join(''),
|
|
16
|
-
type: 'word',
|
|
17
|
-
shape: tokens.map(c => c.shape).join(''),
|
|
18
|
-
sentenceStart: !!tokens.find(t => t.type === 'word' && t.sentenceStart) || false,
|
|
19
|
-
subSentenceStart: !!tokens.find(t => t.type === 'word' && t.subSentenceStart) || false,
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
const preposition = {
|
|
23
|
-
simple: /^(a|about|above|across|after|against|along|among|an|and|around|as|at|before|behind|below|beneath|beside|between|but|by|down|during|for|from|in|inside|into|like|near|nor|of|off|on|onto|or|out|over|so|the|through|to|towards|under|underneath|until|up|upon|with|within|without|yet)$/i,
|
|
24
|
-
complex: /^((according to)|(ahead of)|(apart from)|(as for)|(as of)|(as per)|(as regards)|(aside from)|(back to)|(because of)|(close to)|(due to)|(except for)|(far from)|(inside of)|(instead of)|(next to)|(outside of)|(owing to)|(prior to)|(pursuant to)|(regardless of)|(right of)|(subsequent to))$/i,
|
|
25
|
-
};
|
|
9
|
+
const tokenizer_1 = require("./tokenizer");
|
|
10
|
+
// eslint-disable-next-line no-magic-numbers
|
|
11
|
+
// const show = (obj: any): string => JSON.stringify(obj, null, 2).replace(/[\u007F-\uFFFF]/g, chr => `\\u${(`0000${chr.charCodeAt(0).toString(16)}`).substr(-4)}`)
|
|
26
12
|
function titleCase(s) {
|
|
27
13
|
return s.replace(/^(.)(.+)/, (match, car, cdr) => `${car}${cdr.toLowerCase()}`);
|
|
28
14
|
}
|
|
29
|
-
const Lu = (0, xregexp_1.default)('\\p{Lu}');
|
|
30
|
-
const Ll = (0, xregexp_1.default)('\\p{Ll}');
|
|
31
15
|
const connectedWord = (0, xregexp_1.default)('(^|-)\\p{Lu}\\p{Ll}*(?=-|$)', 'g');
|
|
32
16
|
const connectedInnerWord = (0, xregexp_1.default)('-\\p{Lu}\\p{Ll}*(?=-|$)', 'g');
|
|
33
|
-
|
|
34
|
-
function wordSC(token, succ, allCaps, subSentence) {
|
|
17
|
+
function wordSC(token, allCaps, subSentence) {
|
|
35
18
|
if (token.type !== 'word')
|
|
36
19
|
return token.text;
|
|
37
20
|
if (token.text.match(/^I'/))
|
|
@@ -41,18 +24,16 @@ function wordSC(token, succ, allCaps, subSentence) {
|
|
|
41
24
|
if ((subSentence && token.subSentenceStart) || token.sentenceStart) {
|
|
42
25
|
return allCaps ? titleCase(token.text) : xregexp_1.default.replace(token.text, connectedInnerWord, match => match.toLowerCase());
|
|
43
26
|
}
|
|
27
|
+
if (token.subtype === 'preposition')
|
|
28
|
+
return token.text.toLowerCase();
|
|
29
|
+
if (token.subtype === 'acronym')
|
|
30
|
+
return token.text;
|
|
44
31
|
if (!allCaps && token.shape.match(/^[Xxd]+(-[Xxd]+)+/))
|
|
45
32
|
return xregexp_1.default.replace(token.text, connectedWord, match => match.toLowerCase());
|
|
46
33
|
if (token.text.match(/^[B-Z]$/))
|
|
47
34
|
return token.text;
|
|
48
35
|
if (!allCaps && token.shape.match(/^[-X]+$/))
|
|
49
36
|
return token.text;
|
|
50
|
-
if (succ && `${token.text} ${succ.text}`.match(preposition.complex)) {
|
|
51
|
-
succ.text = succ.text.toLowerCase();
|
|
52
|
-
return token.text.toLowerCase();
|
|
53
|
-
}
|
|
54
|
-
if (token.text.match(preposition.simple))
|
|
55
|
-
return token.text.toLowerCase();
|
|
56
37
|
const shape = token.shape.replace(/[^Xxd]/g, '');
|
|
57
38
|
if (!allCaps && shape.match(/^[Xd-]+$/))
|
|
58
39
|
return token.text;
|
|
@@ -66,110 +47,48 @@ function wordSC(token, succ, allCaps, subSentence) {
|
|
|
66
47
|
return token.text;
|
|
67
48
|
return token.text.toLowerCase();
|
|
68
49
|
}
|
|
69
|
-
function
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
tokens.push({ type: 'whitespace', text, shape: text });
|
|
83
|
-
}
|
|
84
|
-
else if (text) {
|
|
85
|
-
tokens.push({ type: 'punctuation', text, shape: text, start: offset, end: offset + text.length - 1 });
|
|
86
|
-
}
|
|
87
|
-
offset += text.length;
|
|
88
|
-
}
|
|
89
|
-
return tokens;
|
|
90
|
-
}
|
|
91
|
-
function tokenize(title, markup) {
|
|
92
|
-
if (markup)
|
|
93
|
-
title = title.replace(markup, match => '\u2060'.repeat(match.length));
|
|
94
|
-
const doc = (0, one_1.default)(title);
|
|
95
|
-
const tokens = [];
|
|
96
|
-
for (const sentence of doc.json({ offset: true })) {
|
|
97
|
-
const words = [];
|
|
98
|
-
let sentenceStart = true;
|
|
99
|
-
let subSentenceStart = false;
|
|
100
|
-
for (const term of sentence.terms) {
|
|
101
|
-
const m = term.post.match(strayCC);
|
|
102
|
-
if (m) {
|
|
103
|
-
term.text += m[0];
|
|
104
|
-
term.post = term.post.substring(m[0].length);
|
|
105
|
-
term.offset.length += m[0].length;
|
|
50
|
+
function toSentenceCase(title, options = {}) {
|
|
51
|
+
options = Object.assign({ preserveQuoted: true, subSentenceCapitalization: true }, options);
|
|
52
|
+
const allCaps = title === title.toUpperCase();
|
|
53
|
+
const allLower = title === title.toLowerCase();
|
|
54
|
+
if (options.guess && !allCaps && !allLower) {
|
|
55
|
+
let $title = title;
|
|
56
|
+
if (options.nocase)
|
|
57
|
+
$title = $title.replace(options.nocase, match => match.match(/\s/) ? ' ' : '');
|
|
58
|
+
if (options.markup)
|
|
59
|
+
$title = $title.replace(options.markup, '');
|
|
60
|
+
if (options.preserveQuoted) {
|
|
61
|
+
for (const q of [/“.*?”/g, /‘.*?’/g, /".*?"/g]) {
|
|
62
|
+
$title = $title.replace(q, '');
|
|
106
63
|
}
|
|
107
|
-
if (term.pre)
|
|
108
|
-
words.push(...prepost(term.pre, term.offset.start - term.pre.length));
|
|
109
|
-
words.push({
|
|
110
|
-
start: term.offset.start,
|
|
111
|
-
end: term.offset.start + term.offset.length - 1,
|
|
112
|
-
text: term.text,
|
|
113
|
-
type: 'word',
|
|
114
|
-
shape: xregexp_1.default.replaceEach(term.text, [[Lu, 'X', 'all'], [Ll, 'x', 'all'], [/\d/g, 'd']]),
|
|
115
|
-
sentenceStart,
|
|
116
|
-
subSentenceStart,
|
|
117
|
-
});
|
|
118
|
-
sentenceStart = false;
|
|
119
|
-
subSentenceStart = term.post.includes(':');
|
|
120
|
-
if (term.post)
|
|
121
|
-
words.push(...prepost(term.post, term.offset.start + term.offset.length));
|
|
122
64
|
}
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
start += w.length;
|
|
131
|
-
const ss = w === '/' ? [false, false] : [multiple.sentenceStart, multiple.subSentenceStart];
|
|
132
|
-
if (w !== '/')
|
|
133
|
-
multiple.sentenceStart = multiple.subSentenceStart = false;
|
|
134
|
-
return {
|
|
135
|
-
start: started,
|
|
136
|
-
end: start - 1,
|
|
137
|
-
text: w,
|
|
138
|
-
type: w === '/' ? 'punctuation' : 'word',
|
|
139
|
-
shape: xregexp_1.default.replaceEach(w, [[Lu, 'X', 'all'], [Ll, 'x', 'all'], [/\d/g, 'd']]),
|
|
140
|
-
sentenceStart: ss[0],
|
|
141
|
-
subSentenceStart: ss[1],
|
|
142
|
-
};
|
|
143
|
-
}));
|
|
144
|
-
continue;
|
|
65
|
+
let sc = 0;
|
|
66
|
+
const words = (0, tokenizer_1.tokenize)($title);
|
|
67
|
+
const minsc = 2;
|
|
68
|
+
words.forEach((token, i) => {
|
|
69
|
+
// reset for each subsentence
|
|
70
|
+
if (options.subSentenceCapitalization && token.type === 'punctuation' && token.subtype === 'end' && sc < minsc) {
|
|
71
|
+
sc = 0;
|
|
145
72
|
}
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
continue;
|
|
73
|
+
if (token.type === 'word' && token.text.length > 1 && !token.subtype.match(/preposition|acronym|ordinal/) && token.shape.match(/^[^X]*x[^X]*$/)) {
|
|
74
|
+
if (i === 0 || words[i - 1].type === 'whitespace') {
|
|
75
|
+
sc += 1;
|
|
76
|
+
}
|
|
151
77
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
}
|
|
78
|
+
});
|
|
79
|
+
if (sc >= minsc)
|
|
80
|
+
return title;
|
|
156
81
|
}
|
|
157
|
-
return tokens;
|
|
158
|
-
}
|
|
159
|
-
exports.tokenize = tokenize;
|
|
160
|
-
function toSentenceCase(title, options = {}) {
|
|
161
|
-
options = Object.assign({ preserveQuoted: true, subSentenceCapitalization: true }, options);
|
|
162
82
|
title = title.normalize('NFC'); // https://github.com/winkjs/wink-nlp/issues/134
|
|
163
|
-
const allCaps = title === title.toUpperCase();
|
|
164
83
|
if (allCaps && !title.match(/\s/))
|
|
165
84
|
return title;
|
|
166
85
|
let sentenceCased = title;
|
|
167
|
-
const tokens = tokenize(title, options.markup);
|
|
168
|
-
|
|
86
|
+
const tokens = (0, tokenizer_1.tokenize)(title, options.markup);
|
|
87
|
+
for (const token of tokens) {
|
|
169
88
|
if (token.type !== 'whitespace') {
|
|
170
|
-
sentenceCased = sentenceCased.substring(0, token.start) + wordSC(token,
|
|
89
|
+
sentenceCased = sentenceCased.substring(0, token.start) + wordSC(token, allCaps, options.subSentenceCapitalization) + sentenceCased.substring(token.end + 1);
|
|
171
90
|
}
|
|
172
|
-
}
|
|
91
|
+
}
|
|
173
92
|
if (options.markup) {
|
|
174
93
|
for (const match of title.matchAll(options.markup)) {
|
|
175
94
|
sentenceCased = sentenceCased.substring(0, match.index) + match[0] + sentenceCased.substring(match.index + match[0].length);
|
|
@@ -187,17 +106,4 @@ function toSentenceCase(title, options = {}) {
|
|
|
187
106
|
return sentenceCased;
|
|
188
107
|
}
|
|
189
108
|
exports.toSentenceCase = toSentenceCase;
|
|
190
|
-
function guessSentenceCased(title, markup = /<\/?(?:i|b|sup|sub|ncx?)>/g) {
|
|
191
|
-
const noMarkup = title.replace(markup, '');
|
|
192
|
-
if (noMarkup === noMarkup.toUpperCase())
|
|
193
|
-
return false;
|
|
194
|
-
if (noMarkup === noMarkup.toLowerCase())
|
|
195
|
-
return false;
|
|
196
|
-
const words = tokenize(title, markup).filter(token => token.type === 'word' && token.shape.match(/x/i));
|
|
197
|
-
if (!words.length)
|
|
198
|
-
return true;
|
|
199
|
-
const titleCased = words.filter(word => word.shape.match(/^X+$|^X.*x|[^Xx]+-X/));
|
|
200
|
-
return (titleCased.length / words.length) < 0.5; // eslint-disable-line no-magic-numbers
|
|
201
|
-
}
|
|
202
|
-
exports.guessSentenceCased = guessSentenceCased;
|
|
203
109
|
//# sourceMappingURL=sentence-case.js.map
|