['edit', 'submit'].includes(mw.config.get('wgAction')) &&
$.ajax(
'//tools-static.wmflabs.org/meta/scripts/pathoschild.templatescript.js',
{ dataType: 'script', cache: true }
).then(function expandContractions() {
let re;
let edit = editor => {
let escaped = [];
let escape = $0 => `\x7f\x7f\x7f${escaped.push($0) - 1}\x7f\x7f\x7f`;
let unescapeRe = /\x7f{3}(\d+)\x7f{3}/g;
let unescape = ($0, $1) => escaped[$1];
let reps = new Set();
let makeCallback = (w, c) => ($0, $1) => {
reps.add(c);
return $1 + ' ' + w;
};
let orig = editor.get();
let repl = orig
.replace(re, escape)
.replace(
/<!--[^]+?-->|<ref\s[^>]*\/>|<(blockquote|ref|poem)(?:\s?[^>]*)?>[^]+?<\/\1>|\[\[[^\[\]]+?\]\]|["“].+?["”]|(''+).+?\2|^===?\s*(?:References|Sources|Bibliography|Further reading|External links)\s*==[^]+|^=+.+=/gim,
escape
)
.replace(
/\|\s*(?:explain|filename|image|name|reason|song|title)\d*\s*=[^|}]+?(?=[|}])/gi,
escape
)
.replace(
/\b([Hh]e|[Ss]he|[Ii]t|[TWtw]hat|[Tt]here)['’]s(?= (?:(?:not|n?ever|n?either|again|all|almost|also|always|just|often|perhaps|quite|rather|sometimes|soon|still|too|(?!(?:ally|anomaly|apply|assembly|belly|bully|butterfly|chilly|comply|costly|cuddly|curly|disassembly|family|firefly|folly|gainly|gnarly|gully|holy|hurly|imply|jelly|lily|lonely|measly|melancholy|monopoly|multiply|oily|panoply|rally|reassembly|rely|reply|sally|scholarly|silly|sully|supply|surly|tally|ugly|unholy|unruly) )[a-z]{2,}ly) )*(?:appeared|become|been|begun|belonged|come|consisted|existed|got|gotten|had|happened|lasted|occurred|remained|seemed)\b)/g,
makeCallback('has', 's')
)
.replace(
/\b([Hh]e|[Ss]he|[Ii]t|[TWtw]hat|[Tt]here)['’]s\b/g,
makeCallback('is', 's')
)
.replace(/\b([Cc])an['’]t\b/g, '$1annot')
.replace(/\b([Ww])on['’]t\b/g, '$1ill not')
.replace(/\b((?![Aa]i)[A-Z]?[a-z]+)n['’]t\b/g, '$1 not')
.replace(
/\b([Ww]e|[Hh]e|[Ss]he|[Ii]t|[Tt]hey|[TWtw]hat|[Tt]here)['’]d(?= (?:(?:not|n?ever|n?either|again|all|almost|also|always|just|often|perhaps|quite|rather|sometimes|soon|still|too|(?!(?:ally|anomaly|apply|assembly|belly|bully|butterfly|chilly|comply|costly|cuddly|curly|disassembly|family|firefly|folly|gainly|gnarly|gully|holy|hurly|imply|jelly|lily|lonely|measly|melancholy|monopoly|multiply|oily|panoply|rally|reassembly|rely|reply|sally|scholarly|silly|sully|supply|surly|tally|ugly|unholy|unruly) )[a-z]{2,}ly) )*(?:agreed|arisen|awoken|been|begun|blown|bought|broken|brought|built|caught|chosen|dealt|decreed|disagreed|done|drawn|driven|drunk|eaten|fallen|fed|felt|fled|foreseen|forgiven|forgot|forgotten|fought|found|freed|given|gone|got|gotten|grown|guaranteed|had|heard|held|hidden|hurt|kept|known|learnt|led|left|lost|made|meant|met|misheard|mistaken|misunderstood|overheard|overseen|overtaken|paid|rebuilt|rewritten|ridden|risen|said|sat|seen|sent|shot|shown|shrunk|slept|sold|spent|spoken|stolen|stood|struck|stuck|sung|sworn|taken|taught|thought|thrown|told|torn|undergone|understood|undone|woken|won|worn|written|(?![^aeiou]+ed\b|embed\b)[a-z]+[^e]ed)\b)/g,
makeCallback('had', 'd')
)
.replace(
/\b([Ww]e|[Hh]e|[Ss]he|[Ii]t|[Tt]hey|[TWtw]hat|[Tt]here)['’]d\b/g,
makeCallback('would', 'd')
)
.replace(/([a-z])['’]re\b/g, '$1 are')
.replace(/([a-z])['’]ve\b/g, '$1 have')
.replace(/([a-z])['’]ll\b/g, '$1 will')
.replace(unescapeRe, unescape)
.replace(unescapeRe, unescape)
.replace(unescapeRe, unescape);
if (repl === orig) {
mw.notify('No change.');
return;
}
let iw = mw.config.get('wgWikiID') === 'enwiki' ? '' : 'w:en:';
editor
.set(repl)
.appendEditSummary(`expanded contractions using [[${iw}User:Nardog/ExpandContractions|script]]`)
.options({ minor: true })
.clickDiff();
if (reps.size) {
mw.notify(
`Replacements include "'${[...reps].join(`" and "'`)}". Review them before saving.`,
{ type: 'warn' }
);
}
};
let clicked;
window.pathoschild.TemplateScript.add([{
name: 'Expand contractions',
script: editor => {
if (clicked) {
if (re) edit(editor);
return;
}
clicked = true;
let templates = [
'Blockquote', 'Cquote', 'Quote frame', 'Rquote', 'Quote box',
'Poem', 'Poem quote', 'About', 'For', 'Other uses', 'Redirect',
'Distinguish', 'Main', 'Further', 'See also', 'Lang',
'Interlanguage link'
];
let prefix = mw.config.get('wgFormattedNamespaces')[10] + ':';
new mw.Api().get({
action: 'query',
titles: templates.map(n => prefix + n),
redirects: 1,
prop: 'redirects',
rdprop: 'title',
rdnamespace: 10,
rdshow: '!fragment',
rdlimit: 'max',
formatversion: 2
}).always(response => {
let set = new Set(templates);
(((response || {}).query || {}).pages || []).forEach(page => {
[page, ...(page.redirects || [])].forEach(o => {
set.add(o.title.slice(prefix.length));
});
});
re = new RegExp(
`\\{\\{\\s*(?:${
[...set].map(n => {
n = mw.util.escapeRegExp(n);
return n[0].toLowerCase() === n[0]
? n
: '[' + n[0] + n[0].toLowerCase() + ']' + n.slice(1);
}).join('|').replace(/ /g, '[ _]')
})\\s*\\|(?:\\{\\{[^]+?\\}\\}|[^])*?\\}\\}`,
'g'
);
edit(editor);
});
}
}]);
});