['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|already|also|always|earlier|even|far|forever|hence|however|indeed|instead|just|later|least|less|likewise|meanwhile|more|most|much|now|often|once|otherwise|perhaps|quite|rather|seldom|since|so|somehow|sometimes|soon|sooner|still|that|then|thereby|therefore|thus|today|together|too|twice|very|(?!(?: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|arisen|arrived|become|been|begun|behaved|belonged|come|consisted|departed|died|disappeared|elapsed|emerged|existed|expired|got|gotten|had|happened|lacked|lain|lasted|lingered|occurred|persisted|remained|resembled|resulted|seemed|stayed|stood|strive[dn]|struggled|thrived|vanished|waited)\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|already|also|always|earlier|even|far|forever|hence|however|indeed|instead|just|later|least|less|likewise|meanwhile|more|most|much|now|often|once|otherwise|perhaps|quite|rather|seldom|since|so|somehow|sometimes|soon|sooner|still|that|then|thereby|therefore|thus|today|together|too|twice|very|(?!(?: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|beaten|been|begun|bent|bitten|blown|borne?|bought|broken|brought|built|burnt|caught|chosen|clung|crept|dealt|decreed|disagreed|done|drawn|dreamt|driven|drunk|dug|dwelt|eaten|fallen|fed|felt|flown|forbidden|foreseen|forgiven|forgot|forgotten|fought|found|freed|frozen|given|gone|got|gotten|grown|guaranteed|had|heard|held|hidden|hung|kept|known|laid|lain|leapt|learnt|led|left|lent|lit|lost|made|meant|met|misheard|mistaken|misunderstood|overheard|overseen|overtaken|paid|partaken|proven|rebuilt|rewound|rewritten|ridden|risen|rung|said|sank|sat|seen|sent|sewn|shaken|shone|shot|shown|shrunk|slain|slept|slid|snuck|sold|sought|sown|spat|spent|spilt|spoken|sprung|spun|stolen|stood|stricken|struck|strung|stuck|stung|sung|sunk|swept|sworn|swum|swung|taken|taught|thought|thrown|told|torn|undergone|understood|undone|unwound|upheld|withdrawn|withheld|woken|won|worn|woven|written|wrought|(?![^aeiou]+ed\b|embed\b)[a-z]+[a-df-z]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);
});
}
}]);
});