// ==UserScript== // @name Magic™Editor // @author Cameron Bernhardt (AstroCB) // @developer Jonathan Todd (jt0dd) // @developer sathyabhat // @contributor Unihedron // @contributor Tiny Giant // @contributor Mogsdad // @contributor Makyen // @contributor VLAZ // @grant none // @license MIT // @namespace http://github.com/SO-Close-Vote-Reviewers/UserScripts/Magic™Editor // @version 1.8.0 // @description Fix common grammar/usage annoyances on Stack Exchange posts with a click. Forked from https://github.com/AstroCB/Stack-Exchange-Editor-Toolkit // @include /^https?:\/\/([\w-]*\.)*((stackoverflow|stackexchange|serverfault|superuser|askubuntu|stackapps)\.com|mathoverflow.net)\/(c\/[^\/]*\/)?(questions|posts|review|tools)\/(?!tagged\/|new\/).*/ // @exclude *://chat.stackoverflow.com/* // @exclude *://chat.stackexchange.com/* // @exclude *://chat.*.stackexchange.com/* // @exclude *://api.*.stackexchange.com/* // @exclude *://data.stackexchange.com/* // ==/UserScript== /* globals StackExchange, $ */ /* eslint-disable no-multi-spaces, no-sequences */ (function() { "use strict"; function extendEditor(root) { var App = {}; // Place edit items here App.items = {}; App.originals = {}; // Place selected jQuery items here App.selections = {}; // Place "global" app data here App.globals = {}; // Place "const" app data here App.consts = {}; // Place "helper" functions here App.funcs = {}; // True to display counts and / or rule names in Edit Summary App.globals.showCounts = false; App.globals.showRules = false; App.globals.root = root; App.globals.reasons = {}; App.globals.placeHolders = { //The text here is staticly used in some edit RegExp to prevent substitution of placeholders. // See: // badphrases which relies on "_xPlacexHolderx" starting a placeholder. "auto": "_xPlacexHolderxAutoxInsertxTextxPlacexHolderx_", "quote": "_xPlacexHolderxBlockxQuotexPlacexHolderx_", "backtickCode": "_xPlacexHolderxCodexPreserveBlockxPlacexHolderx_", "block": "_xPlacexHolderxCodexBlockxPlacexHolderx_", "blockStart": "_xPlacexHolderxCodexBlockxStartxPlacexHolderx_", "lsec": "_xPlacexHolderxLinkxSectionxPlacexHolderx_", "links": "_xPlacexHolderxLinkxPlacexHolderx_", "preBlock": "_xPlacexHolderxPrexBlockxPlacexHolderx_", "codeTag": "_xPlacexHolderxCodexTagxPlacexHolderx_", "tags": "_xPlacexHolderxTagxPlacexHolderx_", "dashes": "_xPlacexHolderxDashesxPlacexHolderx_" }; App.globals.replacedStrings = {}; App.globals.replacedStringsOriginal = {}; App.globals.placeHolderChecks = {}; App.globals.placeHolderKeys = Object.keys(App.globals.placeHolders); App.globals.checks = { //automatically inserted text // https://regex101.com/r/cI6oK2/1 "auto": /[^]*\<\!\-\- End of automatically inserted text \-\-\>/g, //blockquotes // https://regex101.com/r/fU5lE6/1 "quote": /^\>(?:(?!\n\n)[^])+/gm, //code surrounded by backticks // https://regex101.com/r/8tZD3i/2 "backtickCode": /(?:(?:^(`{3,})[^]+?\1)|(`+)(?:\\`|[^`](?!\n\n))+\2)/gm, //code blocks and multiline inline code. // https://regex101.com/r/eC7mF7/4 "block": /(?:(?:^[ \t]*(?:[\r\n]|\r\n))?`[^`]+`|(?:^[ \t]*(?:[\r\n]|\r\n))^(?:(?:[ ]{4}|[ ]{0,3}\t).+(?:[\r\n]?(?!\n\S)(?:[ \t]+\n)*)+)+)/gm, //code blocks at the start of the post. // https://regex101.com/r/vu7fBd/1 "blockStart": /(?:^(?:(?:[ ]{4}|[ ]{0,3}\t).+(?:[\r\n]?(?!\n\S)(?:[ ]+\n)*)+)+)/g, //link-sections // Testing of this and the "links" RegExp were done within the same regex101.com "regex". // The prior version of this was https://regex101.com/r/tZ4eY3/7 it was saved and became version 21. // It was then forked into it's own regex: // https://regex101.com/r/C7nXfd/2 "lsec": /(?:^ *(?:[\r\n]|\r\n))?(?: {2}(?:\[\d\]): \w*:+\/\/.*\n*)+/gm, //links and pathnames // See comment above the "lsec" RegExp regarding testing sharing the same "regex" on regex101.com // https://regex101.com/r/tZ4eY3/22 "links": /!?\[[^\]\n]+\](?:\([^\)\n]+\)|\[[^\]\n]+\])(?:\](?:\([^\)\n]+\)|\[[^\]\n]+\]))?|(?:\/\w+\/|.:\\|\w*:\/\/|\.+\/[./\w\d]+|(?:\w+\.\w+){2,})[./\w\d:/?#\[\]@!$&'()*+,;=\-~%]*/gi, // ' fix syntax highlighting in code editor //
blocks // https://regex101.com/r/KFvgol/1 "preBlock": /]*?|)>[\W\w]*?<\/pre>/gi,
// blocks
// https://regex101.com/r/waCxWR/1
"codeTag": /]*?|)>[\W\w]*?<\/code>/gi,
// https://regex101.com/r/bF0iQ0/2 tags and html comments
"tags": /\<[\/a-z]+\>|\<\!\-\-[^>]+\-\-\>|\[tag:[\w.-]+\]/gi,
"dashes": /^(\s*--+\s*?)$/gim
};
//Make a shallow copy of the App.globals.checks Object
App.globals.checksr = (function(objIn){
var objOut = {};
var keys = Object.keys(objIn);
for(var i = keys.length-1; i >= 0; --i) objOut[keys[i]] = objIn[keys[i]];
return objOut;
})(App.globals.checks);
// Assign modules here
App.pipeMods = {};
// Define order in which mods affect here
App.globals.order = ["omit", "codefix", "inlineImages", "edit", "diff", "replace", "output"];
// Define reason constant strings
App.consts.reasons = {
legalSO: "'Stack Overflow' is the legal name",
legalSE: "'Stack Exchange' is the legal name",
tagTitle: "removed tags from title",
trademark: "trademark capitalization",
acronym: "acronym capitalization",
spelling: "spelling",
grammar: "grammar",
noise: "noise reduction",
punctuation: "punctuation",
layout: "layout",
silent: "", // Unreported / uncounted
titleSaysAll: "replicated title in body",
inlineImage: "inline image"
};
// Get the original post tags
App.globals.taglist = [];
$('a.post-tag').each( function(){
var newtag = $(this).text();
if (App.globals.taglist.indexOf(newtag) === -1) {
App.globals.taglist.push(newtag);
}
});
// Define edit rules
// See https://regex101.com/r/fC3bY5/2 for a basic RegExp that excludes matches in filenames, paths, library names, etc.
// The following properties are available for each edit rule:
// expr: RegExp Used as the argument for the String methods .match() and the first argument for .replace().
// replacement: String or function Used as the second argument for the String method .replace(). e.g. "$1 want".
// reason: String Should be one of the constants defined as a reason. e.g.: App.consts.reasons.grammar
// rerun: String or Array of String The keys of rules which will be re-run if there are any changes made by this current rule. "rerrun" is executed before "runAfter"
// runBefore: String or Array of String The keys of rules which will be run, perhaps re-run, immediately before this key.
// runAfter: String or Array of String The keys of rules which will be run, perhaps re-run, immediately after this key.
// notAlone: truthy (Boolean) If evaluates to true, then the rule is only run when specified in a "rerun", "runBefore", or "runAfter"
// titleOnly: truthy (Boolean) If evaluates to true, then the rule is only applied to titles.
// bodyOnly: truthy (Boolean) If evaluates to true, then the rule is only applied to bodies.
// debug: truthy (Boolean) If evaluates to true, then debug output is logged to the console for this rule.
// WARNING: rerun, runBefore, and runAfter can result in an infinite loop.
App.edits = {
// Handle all-caps posts first
noneedtoyell: {
expr: /^((?=.*[A-Z])[^a-z]*)$/g,
replacement: function(input) {
return input.trim().substr(0, 1).toUpperCase() + input.trim().substr(1).toLowerCase();
},
reason: App.consts.reasons.grammar
},
// Remove tags from title
taglist: { // https://regex101.com/r/wH4oA3/25
// WARNING: the expression from regex101 must have backslashes escaped here - wbn to automate this...
expr: new RegExp(
"(?:^(?:[(]?(?:_xTagsx_)(?!\\.\\w)(?:and|[ ,.&+/-])*)+[:. \\)-]*|\\b(?:[:. \\(-]|in|with|using|by|for|from)*(?:(?:_xTagsx_)(?:and|[ ,&+/)-])*)+([?.! ]*)$)"
.replace(/_xTagsx_/g,App.globals.taglist.map(escapeTag).join("|")),
//Consider escaping character classes:
//.replace(/\\(?=[bsSdDwW])/g,"\\"), // https://regex101.com/r/pY1hI2/1 - WBN to figure this out.
'gi'
),
replacement: "$1",
debug: false,
titleOnly: true,
reason: App.consts.reasons.tagTitle
},
so: {
expr: /\bstack\s*overflow\b/gi,
replacement: "Stack Overflow",
reason: App.consts.reasons.legalSO
},
se: {
expr: /\bstack\s*exchange\b/gi,
replacement: "Stack Exchange",
reason: App.consts.reasons.legalSE
},
expansionSO: {
expr: /([^\b\w.]|^)SO\b/g,
replacement: "$1Stack Overflow",
reason: App.consts.reasons.legalSO
},
expansionSE: {
expr: /([^\b\w.]|^)SE\b/g,
replacement: "$1Stack Exchange",
reason: App.consts.reasons.legalSE
},
/*
** Trademark names
**/
jsfiddle: {
expr: /\bjs ?fiddle\b/gi,
replacement: "JSFiddle",
reason: App.consts.reasons.trademark
},
meteor: { // must appear before "javascript"
expr: /([^\b\w.]|^)meteor(?: *(js))?\b(?![.-]\w)/gi,
replacement: function (str,pre,uppercase) {
var fixed = pre + "Meteor" + (uppercase ? uppercase.toUpperCase() : '');
return fixed;
},
reason: App.consts.reasons.trademark
},
knockout_js: { // must appear before "javascript"
expr: /\bknockout[. ]?js\b/gi,
replacement: "Knockout.js",
reason: App.consts.reasons.trademark
},
script: { // Spelling rule out-of-order, must run before javascript & google_apps_script
expr: /(s)c[ri]+pt?(ing|s)?\b/gi,
replacement: "$1cript$2",
reason: App.consts.reasons.spelling
},
javascript: {
expr: /([^\b\w.]|^)(java?scr?ipt?|js|java(?:[^\w.]|_)?script?)\b/gi,
replacement: "$1JavaScript",
reason: App.consts.reasons.trademark
},
jquery: {
expr: /\bjque?rr?y\b(?![.-]\w)/gi, // jqury, jquerry, jqurry... ~600 spelling mistakes
replacement: "jQuery",
reason: App.consts.reasons.trademark
},
angularjs: {
expr: /\bangularjs\b(?![.-]\w)/gi, //Updated as Angular and AngularJS are two different things.
replacement: "AngularJS",
reason: App.consts.reasons.trademark
},
angularcli: {
expr: /\bangular\W{0,2}cli\b(?![.-]\w)/gi,
replacement: "Angular CLI",
reason: App.consts.reasons.trademark
},
angular: {
expr: /\bangular\b(?![.-]\w)/gi,
replacement: "Angular",
reason: App.consts.reasons.trademark
},
php: {
expr: /(?:[^\b\w.]|^)php[\d]?\b(?![.-]\w)/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.trademark
},
c: {
expr: /(?:[^\b\w.]|^)c\b(?:#|\+\+)?/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.trademark
},
java: {
expr: /([^\b\w.]|^)java\b(?![.-]\w)/gi,
replacement: "$1Java",
reason: App.consts.reasons.trademark
},
sqlite: {
expr: /\bsql*\W?l*ite(\s*[0-9]*)\b/gi,
replacement: "SQLite$1",
reason: App.consts.reasons.trademark
},
android: {
expr: /\band(?:roi|ori)d\b(?![.-]\w)/gi,
replacement: "Android",
reason: App.consts.reasons.trademark
},
oracle: {
expr: /\boracle\b/gi,
replacement: "Oracle",
reason: App.consts.reasons.trademark
},
windows: {
// https://regex101.com/r/jF9zK1/8
expr: /\b(?:win(?=(?:\s+(?:2k|[0-9.]+|ce|me|nt|xp|vista|server)))|windows)(?:\s+(2k|[0-9.]+|ce|me|nt|xp|vista|server))?\b/gi,
replacement: function(match, ver) {
ver = !ver ? '' : ' ' + ver
.replace(/ce/i, 'CE')
.replace(/me/i, 'ME')
.replace(/nt/i, 'NT')
.replace(/xp/i, 'XP')
.replace(/2k/i, '2000')
.replace(/vista/i, 'Vista')
.replace(/server/i, 'Server');
return 'Windows' + ver;
},
reason: App.consts.reasons.trademark
},
unix: {
expr: /\bunix\b/gi,
replacement: "Unix",
reason: App.consts.reasons.trademark
},
linux: {
expr: /\blinux\b/gi,
replacement: "Linux",
reason: App.consts.reasons.trademark
},
wordpress: {
expr: /\bword ?press\b/gi,
replacement: "WordPress",
reason: App.consts.reasons.trademark
},
mysql: {
expr: /\bmysql\b/gi,
replacement: "MySQL",
reason: App.consts.reasons.trademark
},
nodejs: {
expr: /\bnode\.?js\b/gi,
replacement: "Node.js",
reason: App.consts.reasons.trademark
},
apache: {
expr: /\bapache([\d])?\b(?![.-]\w)/gi,
replacement: "Apache$1",
reason: App.consts.reasons.trademark
},
git: {
expr: /([^\b\w.]|^)git\b/gi,
replacement: "$1Git",
reason: App.consts.reasons.trademark
},
github: {
expr: /\bgithub\b/gi,
replacement: "GitHub",
reason: App.consts.reasons.trademark
},
facebook: { // https://regex101.com/r/rO1tH4/2
expr: /\bf(?:a[cs]e?)?be?o+k?(s)?/gi,
replacement: function(str,s) {
return "Facebook" + (s ? "'s" : "");
},
reason: App.consts.reasons.trademark
},
python: {
//Given that "python" is a real word, this isn't something we necessarily should be capitalizing all the time.
//However, on SO it's far more likely to be the programming language than a snake.
expr: /\bpython\b/gi,
replacement: "Python",
reason: App.consts.reasons.trademark
},
ios: {
expr: /\bios\b/gi,
replacement: "iOS",
reason: App.consts.reasons.trademark
},
iosnum: {
expr: /\bios([0-9])\b/gi,
replacement: "iOS $1",
reason: App.consts.reasons.trademark
},
ubuntu: { // https://regex101.com/r/sT8wV5/2
expr: /\b[uoa]+n?b[uoa]*[tn][oua]*[tnu][oua]*\b/gi,
replacement: "Ubuntu",
reason: App.consts.reasons.trademark
},
vbnet: { // https://regex101.com/r/bB9pP3/8
expr: /(?:vb\.net|\bvb|(?:[^\b\w.]|^)\.net)\b(?:\s*[0-9]+)?\s*(?:framework|core)?/gi,
replacement: function(str) {
return str.replace(/([^.])vb/i, '$1VB')
.replace(/([^.])asp/i, '$1ASP')
.replace(/net/i, 'NET')
.replace(/framework/i, 'Framework')
.replace(/core/i, 'Core');
},
reason: App.consts.reasons.trademark
},
vba_related: {
expr: /(?:[^\b\w.]|^)(?:vba|vbs|vbc|evb|vbo|vbp|vbide)\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.trademark
},
vbscript: {
expr: /\bvbscript/gi,
replacement: "VBScript",
reason: App.consts.reasons.trademark
},
excel: {
expr: /\bexcel\b(?!\-|\.\w)/gi,
replacement: "Excel",
reason: App.consts.reasons.trademark
},
regex: {
expr: /\b(r)egg?([ea]*)x(p)?\b/gi,
replacement: function (match, p1, p2, p3) {
//If this is JavaScript related, then use RegExp
const isRegExp = ['javascript', 'jquery', 'reactjs', 'nodejs'].some(function(testTag) {
return App.globals.taglist.indexOf(testTag) > -1;
});
let result = `${(isRegExp ? 'R' : p1)}eg`;
if ((p2 && p2 === p2.toUpperCase()) || isRegExp) {
result += 'E';
} else {
result += 'e';
}
result += `x${(isRegExp ? 'p' : (p3 || ''))}`;
return result;
},
reason: App.consts.reasons.trademark
},
postgresql: {
expr: /\bpost?gres*(q?l|s)?\b/gi,
replacement: "PostgreSQL",
reason: App.consts.reasons.trademark
},
paypal: {
expr: /\bpaypal\b/gi,
replacement: "PayPal",
reason: App.consts.reasons.trademark
},
tomcat: {
expr: /\btomcat([0-9.]*)/gi,
replacement: "Tomcat$1",
reason: App.consts.reasons.trademark
},
netbeans: {
expr: /\b(?:netbean?|net-bean|net bean|netbeen)s?\b/gi,
replacement: "NetBeans",
reason: App.consts.reasons.trademark
},
nginx: {
expr: /\bnginx\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.trademark
},
firefox: {
expr: /\bfire?fox\b/gi,
replacement: "Firefox",
reason: App.consts.reasons.trademark
},
safari: {
expr: /\bsafari\b/gi,
replacement: "Safari",
reason: App.consts.reasons.trademark
},
chrome: {
expr: /\bchrome\b(?![-.]\w)/gi, //Don't match chrome.* namespace and chrome-* schemes
replacement: "Chrome",
reason: App.consts.reasons.trademark
},
gnu: {
expr: /\bgnu\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.trademark
},
gcc: {
expr: /(?:[^\b\w.]|^)gcc\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.trademark
},
maven: {
expr: /\bmaven\b/gi,
replacement: "Maven",
reason: App.consts.reasons.trademark
},
youtube: {
expr: /\byoutube\b/gi,
replacement: "YouTube",
reason: App.consts.reasons.trademark
},
amazon: {
// https://regex101.com/r/dR0pJ7/1
expr: /\b(amazon(?: )?(?:redshift|web services|cloudfront|console)?)((?: )?(?:ec2|aws|s3|rds|sqs|iam|elb|emr|vpc))?\b/gi,
replacement: function(str,titlecase,uppercase) {
var fixed = toTitleCase(titlecase) + (uppercase ? uppercase.toUpperCase() : '');
return fixed;
},
reason: App.consts.reasons.trademark
},
zend: {
expr: /\bzend((?: )?(?:framework|studio|guard))?\b/gi,
//replacement: toTitleCase(), // Doesn't work like built-in toUpperCase, returns 'undefined'. Load order?
replacement: function(str) {
return toTitleCase(str);
},
reason: App.consts.reasons.trademark
},
twitter: {
expr: /\btwitter\b(?![.-]\w)/gi,
replacement: "Twitter",
reason: App.consts.reasons.trademark
},
bootstrap: { // "bootstrap" is also a general computing term, so expect some false positives
expr: /\bbootst?r?ap\b/gi,
replacement: "Bootstrap",
reason: App.consts.reasons.trademark
},
apple: {
expr: /\bapple\b/g,
replacement: "Apple",
reason: App.consts.reasons.trademark
},
iphone: {
expr: /\biph?one?\b/gi,
replacement: "iPhone",
reason: App.consts.reasons.trademark
},
google: { // https://regex101.com/r/qW8fI8/4
expr: /\bgo+(?:g+le?|lge?|gl?el)(e[drs]*|ing)\b/gi,
replacement: "Googl$1",
reason: App.consts.reasons.trademark
},
google_verbed: {
expr: /\bgoogl(?:ed|ing|er)\b/gi,
replacement: function(str) {
return toTitleCase(str);
},
reason: App.consts.reasons.trademark
},
spreadsheet: { // https://regex101.com/r/oK4uW3/1 - must appear before google_things
expr: /\b(s)[pr]+[ea]+dsh?e+t(?:ing)?(s)?\b/gi,
replacement: "$1preadsheet$2",
reason: App.consts.reasons.spelling
},
google_things: { // https://regex101.com/r/iS5fO1/1
expr: /\bgoogle\b[ \t-]*(?:maps?|sheets?|docs?|drive|sites?|forms?|documents?|spreadsheets?|images?|presentations?|play)?\b/gi,
replacement: function(str) {
return toTitleCase(str);
},
reason: App.consts.reasons.trademark
},
google_apps_script: { //Not in google_things due to possible missing 's' on Apps.
expr: /\bgoogle[- ]?(?:apps?)?[- ]?script(ing|s)?\b/gi,
replacement: "Google Apps Script$1",
reason: App.consts.reasons.trademark
},
google_app_engine: { //Not in google_things due to possible 's' on App.
expr: /\bgoogle[- ]?(?:apps?)?[- ]?engine(s)?\b/gi,
replacement: "Google App Engine$1",
reason: App.consts.reasons.trademark
},
google_analytics: { //Not in google_things due to possible missing 's' on analytics.
expr: /\bgoogle[- ]?analytics?\b/gi,
replacement: "Google Analytics",
},
bluetooth: {
expr: /\bbl(?:ue|oo)too?th?\b/gi,
replacement: "Bluetooth",
reason: App.consts.reasons.trademark
},
lenovo: {
expr: /\bleno?vo\b/gi,
replacement: "Lenovo",
reason: App.consts.reasons.trademark
},
matlab: {
expr: /([^\b\w.]|^)math?lab\b/gi,
replacement: "$1MATLAB",
reason: App.consts.reasons.trademark
},
internet: {
expr: /\binternet\b/g,
replacement: "Internet",
reason: App.consts.reasons.trademark
},
oauth: { // https://regex101.com/r/sA2cQ5/1
expr: /\boauth(?:(?: )*(\d)(?!\.\d)|(?: )*([\d.]+))?\b/gi,
replacement: "OAuth$1 $2",
reason: App.consts.reasons.trademark
},
web_services: {
expr: /\bweb services\b/g,
replacement: "Web services",
reason: App.consts.reasons.trademark
},
opencv: {
expr: /\bopencv\b/gi,
replacement: "OpenCV",
reason: App.consts.reasons.trademark
},
ruby: {
expr: /\bruby\b/g,
replacement: "Ruby",
reason: App.consts.reasons.trademark
},
rails: {
expr: /\brails\b/g,
replacement: "Rails",
reason: App.consts.reasons.trademark
},
grails: {
expr: /\bgrails\b/g,
replacement: "Grails",
reason: App.consts.reasons.trademark
},
subversion: {
expr: /\bsubvers[io]*n\b/g,
replacement: "Subversion",
reason: App.consts.reasons.trademark
},
javafx: {
expr: /\bjavafx\b/gi,
replacement: "JavaFX",
reason: App.consts.reasons.trademark
},
delphi: {
expr: /\bdelphi\b/gi,
replacement: "Delphi",
reason: App.consts.reasons.trademark
},
dotnetnuke: {
expr: /\bdotnetnuke\b/gi,
replacement: "DotNetNuke",
reason: App.consts.reasons.trademark
},
silverlight: {
expr: /\bsilv?erl(?:ight|ite)\b/gi,
replacement: "Silverlight",
reason: App.consts.reasons.trademark
},
scipy: {
expr: /([^\b\w.]|^)scipy\b/gi,
replacement: "$1SciPy",
reason: App.consts.reasons.trademark
},
numpy: {
expr: /([^\b\w.]|^)numpy\b/gi,
replacement: "$1NumPy",
reason: App.consts.reasons.trademark
},
openssl: {
expr: /([^\b\w.]|^)openssl\b/gi,
replacement: "$1OpenSSL",
reason: App.consts.reasons.trademark
},
drupal: {
expr: /([^\b\w.]|^)drupal\b/gi,
replacement: "$1Drupal",
reason: App.consts.reasons.trademark
},
saas: {
expr: /([^\b\w.]|^)saas\b/gi,
replacement: "$1SaaS",
reason: App.consts.reasons.trademark
},
gwt: {
expr: /([^\b\w.]|^)gwt[- ](mosaic|designer)?\b/gi,
replacement: function (str,pre,titlecase) {
var fixed = pre + "GWT" + (titlecase ? ' ' + toTitleCase(titlecase) : ' ');
return fixed;
},
reason: App.consts.reasons.trademark
},
gmail: {
expr: /([^\b\w.]|^)gmail(s)?\b/gi,
replacement: "$1Gmail$2",
reason: App.consts.reasons.trademark
},
xampp: {
expr: /([^\b\w.]|^)xam+p+\b/gi,
replacement: "$1XAMPP",
reason: App.consts.reasons.trademark
},
galaxy: {
expr: /([^\b\w.]|^)galaxy\b/gi,
replacement: "$1Galaxy",
reason: App.consts.reasons.trademark
},
mongo: {
expr: /([^\b\w.]|^)mongo(?:\s?(db))?\b/gi,
replacement: function(str,pre,uppercase) {
var fixed = pre + "Mongo" + (uppercase ? uppercase.toUpperCase() : '');
return fixed;
},
reason: App.consts.reasons.trademark
},
pymongo: {
expr: /([^\b\w.]|^)pymongo\b/gi,
replacement: "$1PyMongo",
reason: App.consts.reasons.trademark
},
scala: {
expr: /([^\b\w.]|^)scala\b/gi,
replacement: "$1Scala",
reason: App.consts.reasons.trademark
},
microsoft: { // https://regex101.com/r/dJ5tE3/1
expr: /\b([mM]icrosoft?|[mM]ircosoft|M[Ss]oft)\b/g,
replacement: "Microsoft",
reason: App.consts.reasons.trademark
},
intellisense: {
expr: /\bintell?isen[sc]e?\b/gi,
replacement: "IntelliSense",
reason: App.consts.reasons.trademark
},
sass: { // Syntactically Awesome Style Sheets
expr: /\bsass\b/gi,
replacement: "Sass",
reason: App.consts.reasons.trademark
},
heroku: {
expr: /\bheroku\b/gi,
replacement: "Heroku",
reason: App.consts.reasons.trademark
},
os_x: {
expr: /\bos ?x\b/gi,
replacement: "OS X",
reason: App.consts.reasons.trademark
},
el_capitan: {
expr: /\bel ?capi?tan\b/gi,
replacement: "El Capitan",
reason: App.consts.reasons.trademark
},
hadoop: {
expr: /\bhad+o+p+\b/gi,
replacement: "Hadoop",
reason: App.consts.reasons.trademark
},
django: {
expr: /\bdjango\b/gi,
replacement: "Django",
reason: App.consts.reasons.trademark
},
tcl: {
expr: /([^\b\w.]|^)tcl\b/gi,
replacement: "$1Tcl",
reason: App.consts.reasons.trademark
},
flickr: {
expr: /\bflickr(?!\.\w)/gi,
replacement: "Flickr",
reason: App.consts.reasons.trademark
},
poi: {
expr: /(?:[^\b\w.]|^)poi\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.trademark
},
vmware: {
expr: /\bvmware?\b/gi,
replacement: "VMware",
reason: App.consts.reasons.trademark
},
hortonworks: {
expr: /([^\b\w.]|^)horton ?works[- ](sandbox|data platform|phoenix|hive)?\b/gi,
replacement: function (str,pre,titlecase) {
var fixed = pre + "Hortonworks" + (titlecase ? ' ' + toTitleCase(titlecase) : ' ');
return fixed;
},
reason: App.consts.reasons.trademark
},
ambari: {
expr: /\bambari\b/gi,
replacement: "Ambari",
reason: App.consts.reasons.trademark
},
eclipse: {
expr: /\becli[ps]+e\b/gi,
replacement: "Eclipse",
reason: App.consts.reasons.trademark
},
pthread: {
expr: /([^\w.\-/\\_]|^)pthr[ea]+d(s)?\b(?![.\-]\w|[/\\_])/gi,
replacement: "$1Pthread$2",
reason: App.consts.reasons.trademark
},
perl: {
expr: /([^\w.\-/\\_]|^)perl\b(?![.\-]\w|[/\\_])/gi,
replacement: "$1Perl",
reason: App.consts.reasons.trademark
},
htc: {
expr: /\bhtc\b/gi,
replacement: "HTC",
reason: App.consts.reasons.trademark
},
greasemonkey: {
expr: /\bgre[ea]se\W?monkey\b/gi, //Should this also be correcting spelling, or should that be a separate rule?
replacement: "Greasemonkey",
reason: App.consts.reasons.trademark
},
tampermonkey: {
expr: /\btamper\W?monkey\b/gi,
replacement: "Tampermonkey",
reason: App.consts.reasons.trademark
},
violentmonkey: {
expr: /\bviolent\W?monkey\b/gi,
replacement: "Violentmonkey",
reason: App.consts.reasons.trademark
},
mozilla: {
expr: /\bmozill?a\b/gi,
replacement: "Mozilla",
reason: App.consts.reasons.trademark
},
webextensions: {
expr: /\bweb-*extension(s*)\b/gi,
replacement: "WebExtension$1",
reason: App.consts.reasons.trademark
},
firefoxWebextensions: {
expr: /\bfirefox[ \-]*web[ \-]*exten[st]ion(s*)\b/gi,
replacement: "Firefox WebExtension$1",
reason: App.consts.reasons.trademark
},
microsoftedge: {
expr: /\bmicrosoft[ \-]*edge\b/gi,
replacement: "Microsoft Edge",
reason: App.consts.reasons.trademark
},
typescript: {
expr: /\btypescript\b/gi,
replacement: "TypeScript",
reason: App.consts.reasons.trademark
},
xulrunner: {
expr: /\bxulrunner\b/gi,
replacement: "XULRunner",
reason: App.consts.reasons.trademark
},
xul: {
expr: /\bxul\b/gi,
replacement: "XUL",
reason: App.consts.reasons.trademark
},
webrtc: {
expr: /\bwebrtc\b/gi,
replacement: "WebRTC",
reason: App.consts.reasons.trademark
},
cakephp: {
expr: /\bcakephp\b/gi,
replacement: "CakePHP",
reason: App.consts.reasons.trademark
},
usps: {
expr: /\busps\b/gi,
replacement: "USPS",
reason: App.consts.reasons.trademark
},
ups: {
expr: /\bups\b/gi,
replacement: "UPS",
reason: App.consts.reasons.trademark
},
fedex: {
expr: /\bFedEx\b/gi,
replacement: "FedEx",
reason: App.consts.reasons.trademark
},
shopify: {
expr: /\bshopify\b/gi,
replacement: "Shopify",
reason: App.consts.reasons.trademark
},
xcode: {
expr: /\bxcode\b/gi,
replacement: "Xcode",
reason: App.consts.reasons.trademark
},
imagemagic: {
expr: /\bimagemagic\b/gi,
replacement: "ImageMagic",
reason: App.consts.reasons.trademark
},
openfire: {
expr: /\bopenfire\b/gi,
replacement: "Openfire",
reason: App.consts.reasons.trademark
},
wifi: {
expr: /\bwi-?fi\b/gi,
replacement: "Wi-Fi",
reason: App.consts.reasons.trademark
},
springboot: {
expr: /\bspring ?boot\b/gi,
replacement: "Spring Boot",
reason: App.consts.reasons.trademark
},
springcloud: {
expr: /\bspring ?cloud\b/gi,
replacement: "Spring Cloud",
reason: App.consts.reasons.trademark
},
jmeter: {
expr: /\bjmeter\b/gi,
replacement: "JMeter",
reason: App.consts.reasons.trademark
},
digitalocean: {
expr: /\bdigital\W?ocean\b/gi,
replacement: "DigitalOcean",
reason: App.consts.reasons.trademark
},
orangehrm: {
expr: /\borange\W?hrm\b/gi,
replacement: "OrangeHRM",
reason: App.consts.reasons.trademark
},
codeigniter: {
expr: /\bcode\W?igniter\b/gi,
replacement: "CodeIgniter",
reason: App.consts.reasons.trademark
},
openvpn: {
expr: /\bopenvpn(\d?)\b/gi,
replacement: "OpenVPN$1",
reason: App.consts.reasons.trademark
},
tensorflow: {
expr: /\btensor\W?flow\b/gi,
replacement: "TensorFlow",
reason: App.consts.reasons.trademark
},
netsuite: {
expr: /\bnetsuite\b/gi,
replacement: "NetSuite",
reason: App.consts.reasons.trademark
},
cpanel: {
expr: /\bcpanel\b/gi,
replacement: "cPanel",
reason: App.consts.reasons.trademark
},
putty: {
expr: /\bputty\b/gi,
replacement: "PuTTY",
reason: App.consts.reasons.trademark
},
godaddy: {
expr: /\bgodaddy\b/gi,
replacement: "GoDaddy",
reason: App.consts.reasons.trademark
},
cryptoapi: {
expr: /\bcrypto\s?api\b/gi,
replacement: "CryptoAPI",
reason: App.consts.reasons.trademark
},
selenium: {
expr: /\bselenium\b/gi,
replacement: "Selenium",
reason: App.consts.reasons.trademark
},
testng: {
expr: /\btest\s?ng\b/gi,
replacement: "TestNG",
reason: App.consts.reasons.trademark
},
ionic: {
expr: /\bionic(?:\s?pro)?\b/gi,
replacement: function(str) {
return toTitleCase(str);
},
reason: App.consts.reasons.trademark
},
opencart: {
expr: /\bopen\s?cart\b/gi,
replacement: "OpenCart",
reason: App.consts.reasons.trademark
},
woocommerce: {
expr: /\bwoo\s?commerce\b/gi,
replacement: "WooCommerce",
reason: App.consts.reasons.trademark
},
laravel: {
expr: /\blaravel/gi,
replacement: "Laravel",
reason: App.consts.reasons.trademark
},
pfsense: {
expr: /\bpfsense\b/gi,
replacement: "pfSense",
reason: App.consts.reasons.trademark
},
mipsN: {
expr: /\bmips(32|64)?\b/gi,
replacement: "MIPS$1",
reason: App.consts.reasons.trademark
},
armN: {
expr: /\barm(32|64)\b/gi, //arm by itself is too generic to automatically capitalize
replacement: "ARM$1",
reason: App.consts.reasons.trademark
},
powerpcN: {
expr: /\bpowerpc(32|64)?\b/gi,
replacement: "PowerPC$1",
reason: App.consts.reasons.trademark
},
android_studio: {
expr: /\bandroid ?studio\b/gi,
replacement: "Android Studio",
reason: App.consts.reasons.trademark
},
arduino: {
expr: /\barduino(s?)\b/gi,
replacement: "Arduino$1",
reason: App.consts.reasons.trademark
},
crashlytics: {
expr: /\bcrashl[yi]tics?\b/gi,
replacement: "Crashlytics",
reason: App.consts.reasons.trademark
},
firebase: {
expr: /\bfirebase\b/gi,
replacement: "Firebase",
reason: App.consts.reasons.trademark
},
whatsapp: {
expr: /\bwhatsapp\b/gi,
replacement: "WhatsApp",
reason: App.consts.reasons.trademark
},
atlassian: {
expr: /\batl[ae]s+ian\b/gi,
replacement: "Atlassian",
reason: App.consts.reasons.trademark
},
confluence: {
expr: /\bconfluence\b/gi,
replacement: "Confluence",
reason: App.consts.reasons.trademark
},
woocommerce: {
expr: /\bcwoocommerce\b(?!\.\S)/gi,
replacement: "WooCommerce",
reason: App.consts.reasons.trademark
},
/*
** Acronyms - to be capitalized (except sometimes when part of a file name)
**/
x_html: {
expr: /(?:[^\b\w.]|^)(:?g|ht|xa?|xht|sf|csht)ml[\d.]*\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
css: {
expr: /(?:[^\b\w.]|^)s?css\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
json: {
expr: /(?:[^\b\w.]|^)json\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
ajax: {
expr: /\bajax\b/g, // Leave "Ajax" alone. See https://github.com/AstroCB/Stack-Exchange-Editor-Toolkit/issues/45
replacement: "AJAX",
reason: App.consts.reasons.acronym
},
sql: {
expr: /(?:[^\b\w.]|^)sql\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
urli: {
expr: /\b(ur[li])(s)?\b/gi,
replacement: function(match,upper,lower) { return upper.toUpperCase() + (lower?lower.toLowerCase():''); },
reason: App.consts.reasons.acronym
},
asp: {
expr: /([^\b\w.]|^)asp\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
pdf: {
expr: /([^\b\w.]|^)pdf(s)?/gi,
replacement: "$1PDF$2",
reason: App.consts.reasons.acronym
},
api: {
expr: /([^\b\w.]|^)api(s)?\b/gi,
replacement: "$1API$2",
reason: App.consts.reasons.acronym
},
ssl: {
expr: /(?:[^\b\w.]|^)ssl\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
npm: {
expr: /\bnpm(s)?\b/g,
replacement: "NPM$1",
reason: App.consts.reasons.acronym
},
ftp: {
expr: /(?:[^\b\w.]|^)[st]?ftps?\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
ipa: {
expr: /(?:[^\b\w.]|^)ipa\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
avl: {
expr: /(?:[^\b\w.]|^)avl\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
cli_cgi: {
expr: /(?:[^\b\w.]|^)c[lg]i\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
dll: {
expr: /(?:[^\b\w.]|^)dll\b/g,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
mp3_mp4: {
expr: /([^\b\w.]|^)mp(3|4)(s)?\b/gi,
replacement: "$1MP$2$3",
reason: App.consts.reasons.acronym
},
gui: {
expr: /([^\b\w.]|^)gui(s)?\b/gi,
replacement: "$1GUI$2",
reason: App.consts.reasons.acronym
},
stp: {
expr: /(?:[^\b\w.]|^)stp\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
tcp: {
expr: /(?:[^\b\w.]|^)tcp\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
wpf: {
expr: /(?:[^\b\w.]|^)wpf\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
http: {
expr: /(?:[^\b\w.]|^)https?\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
woff: {
expr: /(?:[^\b\w.]|^)woff\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
ttf: {
expr: /(?:[^\b\w.]|^)ttf\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
ipv_n: {
expr: /\bip(v[46])?\b/gi,
replacement: "IP$1",
reason: App.consts.reasons.acronym
},
fq_dn_s: { // FQDN, DN, DNS
expr: /(?:[^\b\w.]|^)(?:fq)?dns?\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
icmp: {
expr: /\bicmp\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
rsvp: {
expr: /\brsvp\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
snmp: {
expr: /\bsnmp\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
cpu: {
expr: /\bcpu(s)?\b/gi,
replacement: "CPU$1",
reason: App.consts.reasons.acronym
},
rss: {
expr: /(?:[^\b\w.]|^)rss?\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
mvc: {
expr: /(?:[^\b\w.]|^)mvc\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
mvn: {
expr: /(?:[^\b\w.]|^)mvn\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
ascii: {
expr: /([^\b\w.]|^)ascc?ii?\b/gi,
replacement: "$1ASCII",
reason: App.consts.reasons.acronym
},
gsoap: {
expr: /([^\b\w.]|^)gsoap\b/gi,
replacement: "$1gSOAP",
reason: App.consts.reasons.acronym
},
soap: {
expr: /([^\b\w.]|^)soap\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
csv: {
expr: /([^\b\w.]|^)csv\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
image_types: {
expr: /([^\b\w.]|^)(gif|jpe?g|bmp|png)\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
yaml: {
expr: /([^\b\w.]|^)yaml\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
smtp: {
expr: /\bsmtp\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
phpmyadmin: {
expr: /([^\b\w.]|^)phpmyadmin\b/gi,
replacement: "$1phpMyAdmin",
reason: App.consts.reasons.acronym
},
phpunit: {
expr: /([^\b\w.]|^)phpunit\b/gi,
replacement: "$1PHPUnit",
reason: App.consts.reasons.acronym
},
mkl: {
expr: /([^\b\w.]|^)mkl\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
xsl: {
expr: /(?:[^\b\w.]|^)xslt?(?!:)\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
jpa: {
expr: /(?:[^\b\w.]|^)jpa\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
jvm: {
expr: /(?:[^\b\w.]|^)jvm\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
linq: {
expr: /(?:[^\b\w.]|^)linq\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
md5: {
expr: /(?:[^\b\w.]|^)md5\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
xfa_xsd: { // XML Forms Architecture
expr: /(?:[^\b\w.]|^)xfa|xsd\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
wsdl: {
expr: /(?:[^\b\w.]|^)wsdl\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
hdp: { // Hadoop related acronyms
expr: /(?:[^\b\w.]|^)h(?:dp|dfs|sm)\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
ide: {
expr: /(?:[^\b\w.]|^)ide\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
ram_rom: {
expr: /(?:[^\w.\-/\\_]|^)r[ao]m\b(?![.\-]\w|[/\\_])/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
sdk: {
expr: /(?:[^\b\w.]|^)sdk\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
usb: {
expr: /(?:[^\b\w.]|^)usb\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
utf: {
expr: /(?:[^\b\w.]|^)utf\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
xmpp: {
expr: /(?:[^\b\w.]|^)xmpp\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
seo: {
expr: /(?:[^\b\w.]|^)seo\b/gi,
replacement: function (match) { return match.toUpperCase(); },
reason: App.consts.reasons.acronym
},
gps: {
expr: /\bgps\b/gi,
replacement: "GPS",
reason: App.consts.reasons.acronym
},
vps: {
expr: /\bvps\b/gi,
replacement: "VPS",
reason: App.consts.reasons.acronym
},
cisc: {
expr: /\bcisc\b/gi,
replacement: "CISC",
reason: App.consts.reasons.acronym
},
risc: {
expr: /\brisc\b/gi,
replacement: "RISC",
reason: App.consts.reasons.acronym
},
midi: {
expr: /\bmidi\b/gi,
replacement: "MIDI",
reason: App.consts.reasons.acronym
},
cdn: {
expr: /\bcdn\b/gi,
replacement: "CDN",
reason: App.consts.reasons.acronym
},
/*
** Spelling - Correct common spelling errors. (Including apostrophes, which are really grammar.)
** Acknowledgement: A subset of terms were adapted from Peter Mortensen's list
** (http://pvm-professionalengineering.blogspot.de/2011/04/word-list-for-editing-stack-exchange.html)
**/
voting: {
expr: /\b(down|up)\Wvot/gi,
replacement: "$1vote",
reason: App.consts.reasons.spelling
},
succeed: {
expr: /\b(s)uc[cs]?ee?d(ed|s)?\b/gi,
replacement: "$1ucceed$2",
reason: App.consts.reasons.spelling
},
source: {
expr: /\b(s)orce(s|d)?\b/gi,
replacement: "$1ource$2",
reason: App.consts.reasons.spelling
},
standardize: { // https://regex101.com/r/vN7pM0/1
expr: /\b(s)tandari([sz](?:e|es|ed|ation))\b/gi,
replacement: "$1tandardi$2",
reason: App.consts.reasons.spelling
},
different: { // https://regex101.com/r/xO8jU2/1
expr: /\b(d)iff?e?re?n(t|ces?)\b/gi,
replacement: "$1ifferen$2",
reason: App.consts.reasons.spelling
},
personally: { // https://regex101.com/r/oL9aM1/2
expr: /\b(p)erso(?:nl|nl|nal)(ly)?\b/gi,
replacement: "$1ersonal$2",
reason: App.consts.reasons.spelling
},
problem: { // https://regex101.com/r/yA8jM7/6
expr: /\b(p)(?:or?|ro|rο|r0)b(?:le|el|e|re|l|[|]e)me?(s)?\b/gi,
replacement: "$1roblem$2",
reason: App.consts.reasons.spelling
},
written: {
expr: /\b(w)riten\b/gi,
replacement: "$1ritten",
reason: App.consts.reasons.spelling
},
maybe: {
expr: /\b(m)(?:aby|yabe)\b/gi,
replacement: "$1aybe",
reason: App.consts.reasons.spelling
},
pseudo: {
expr: /\b(p)suedo\b/gi,
replacement: "$1seudo",
reason: App.consts.reasons.spelling
},
application: { // https://regex101.com/r/bO4dP4/3
expr: /\b(a)p[plia]+ca?[tio]+n(s)?\b/gi,
replacement: "$1pplication$2",
reason: App.consts.reasons.spelling
},
calendar: {
expr: /\b(c)al[ea]nd[ae]r\b/gi,
replacement: "$1alendar",
reason: App.consts.reasons.spelling
},
commit: { // https://regex101.com/r/kY6sN8/1
expr: /\b(c)omm?it?(s|ted|ters?|ting)?\b/gi,
replacement: "$1ommit$2",
reason: App.consts.reasons.spelling
},
autocomplete: { // https://regex101.com/r/rZ9gW5/1
expr: /\b(a)uto?[ -]?co?m?p?l?ete?(s)?\b/gi,
replacement: "$1utocomplete$2",
reason: App.consts.reasons.spelling
},
you: {
expr: /\b(y)o+u?\b/gi,
replacement: "$1ou",
reason: App.consts.reasons.spelling
},
doesn_t: { // https://regex101.com/r/sL0uO9/5
expr: /\b(d)(?:ose?[^\w]*n?.?t|oens.?t|oesn?[^\w]*t|oest)\b/gi,
replacement: "$1oesn't",
reason: App.consts.reasons.spelling
},
couldn_t_wouldn_t_shouldn_t: {
expr: /\b(c|w|sh)o?ul?dn[ '`´]*t\b/gi,
replacement: "$1ouldn't",
reason: App.consts.reasons.spelling
},
didn_t: {
expr: /\b(d)id?[^\w]*n?t\b/gi, // Caveat: changes dint -> didn't, although "dint" is a word.
replacement: "$1idn't",
reason: App.consts.reasons.spelling
},
don_t: { // https://regex101.com/r/nT2jV6/1
expr: /\b(d)(?:on[^\w']*t|o[n']+o?t)\b/gi,
replacement: "$1on't",
reason: App.consts.reasons.spelling
},
haven_t: {
expr: /\b(h)(?:avent|av[^\w]*t|ave[^\w]?t)\b/gi,
replacement: "$1aven't",
reason: App.consts.reasons.spelling
},
wasn_t: {
expr: /\b(w)as[^\w]*n?t\b/gi,
replacement: "$1asn't",
reason: App.consts.reasons.spelling
},
//apostrophe_d: { // Too many false positives
// expr: /\b(he|she|who|you)[^\w]*(d)\b/gi,
// replacement: "$1'$2",
// reason: App.consts.reasons.spelling
//},
apostrophe_ll: {
expr: /\b(they|what|who|you)[^\w]*(ll)\b/gi,
replacement: "$1'$2",
reason: App.consts.reasons.spelling
},
apostrophe_re: {
expr: /\b(they|what|you)[^\w]*(re)\b/gi,
replacement: "$1'$2",
reason: App.consts.reasons.spelling
},
apostrophe_s: { // https://regex101.com/r/bN5pA3/1
expr: /\b(he|she|that|there|what|where|here)[^\w]*(s)\b/gi,
replacement: "$1'$2",
reason: App.consts.reasons.spelling
},
it_s: {
expr: /\b(it)[^\w](s)\b/gi,
replacement: "$1'$2",
reason: App.consts.reasons.spelling
},
apostrophe_t: {
expr: /\b(aren|can|couldn|didn|doesn|don|hasn|haven|isn|mightn|mustn|shan|shouldn|won|wouldn)[^\w]*(t)(?:[^\w]t)*\b/gi,
replacement: "$1'$2",
reason: App.consts.reasons.spelling
},
apostrophe_nt: {
expr: /['`´]nt\b/gi,
replacement: "n't",
reason: App.consts.reasons.spelling
},
doesn_t_work: { // >4K instances of this (Oct 2015)
expr: /\b(d)oesn[^\w]t (work|like|think|want|put|save|load|get|help|make)s\b/gi,
replacement: "$1oesn't $2",
reason: App.consts.reasons.spelling
},
probably: { // https://regex101.com/r/zU3qZ0/1
expr: /\b(p)r(?:oll?|obb?l|o?babl?|ababl)y\b/gi,
replacement: "$1robably",
reason: App.consts.reasons.spelling
},
keyboard: {
expr: /\b(k)ey?boa?rd\b/gi,
replacement: "$1eyboard",
reason: App.consts.reasons.spelling
},
ur: {
expr: /\bur\b/gi,
replacement: "your", // May also be "you are", but less common on SO
reason: App.consts.reasons.spelling
},
u: {
expr: /\bu\b/gi,
replacement: "you",
reason: App.consts.reasons.spelling
},
gr8: {
expr: /\bgr8\b/gi,
replacement: "great",
reason: App.consts.reasons.spelling
},
cuz: {
expr: /'?\bcuz\b|'cause\b/gi,
replacement: "because",
reason: App.consts.reasons.spelling
},
because_: { // 10K+ posts
expr: /\b(c)ause (?=I|you|we|if)\b/gi,
replacement: "because ",
reason: App.consts.reasons.spelling
},
ofc: {
expr: /\b(o)fc\b/gi,
replacement: "$1f course",
reason: App.consts.reasons.spelling
},
nvm: {
expr: /\b(n)vm\b/gi,
replacement: "$1ever mind",
reason: App.consts.reasons.spelling
},
btw: {
expr: /\b(b)tw,?\b/gi,
replacement: "$1y the way,",
reason: App.consts.reasons.spelling
},
sry: {
expr: /\b(s)o?r+y\b/gi,
replacement: "$1orry",
reason: App.consts.reasons.spelling
},
any1: {
expr: /\b(a)ny1\b/gi,
replacement: "$1nyone",
reason: App.consts.reasons.spelling
},
allways: {
expr: /\b(a)llways\b/gi,
replacement: "$1lways",
reason: App.consts.reasons.spelling
},
expect: {
expr: /\b(e)spect(s)?\b/gi,
replacement: "$1xpect$2",
reason: App.consts.reasons.spelling
},
employee: {
expr: /\b(e)mploye\b/gi,
replacement: "$1mployee",
reason: App.consts.reasons.spelling
},
retrieve: {
expr: /\b(r)etreiv(e|ed|es|ing|al|able)\b/gi,
replacement: "$1etriev$2",
reason: App.consts.reasons.spelling
},
success: { // https://regex101.com/r/hK2vG4/1
expr: /\b(s)ucc?ess?(ful|fully)?l?\b/gi,
replacement: "$1uccess$2",
reason: App.consts.reasons.spelling
},
anyones: {
expr: /\b(a)nyones\b/gi,
replacement: "$1nyone's",
reason: App.consts.reasons.spelling
},
length: {
expr: /\b(l)en(?:gh?t|th)\b/gi,
replacement: "$1ength",
reason: App.consts.reasons.spelling
},
height: {
expr: /\b(h)(?:ei|i|ie)(?:gt|th|ghth|gth)\b/gi,
replacement: "$1eight",
reason: App.consts.reasons.spelling
},
width: {
expr: /\b(w)it?dh?t\b/gi,
replacement: "$1idth",
reason: App.consts.reasons.spelling
},
aint_isnt: {
expr: /\bain'?t\b/gi,
replacement: "isn't",
reason: App.consts.reasons.spelling
},
coordinates: {
expr: /\b(c)ordinate(s|d)?\b/gi,
replacement: "$1oordinate$2",
reason: App.consts.reasons.spelling
},
argument: { // https://regex101.com/r/iU2vK9/2
expr: /\b(a)rg?[ue]+m[ea]nt(s)?\b/gi,
replacement: "$1rgument$2",
reason: App.consts.reasons.spelling
},
iterate: { // https://regex101.com/r/iL6bV3/1
expr: /\b(i)(?:tter|tar)at(e[ds]?|ing|ion|ions)\b/gi,
replacement: "$1terat$2",
reason: App.consts.reasons.spelling
},
below: {
expr: /\b(b)ellow\b/gi, // "Bellow" is a word, but extremely uncommon on StackOverflow.com.
replacement: "$1elow",
reason: App.consts.reasons.spelling
},
encrypt: {
expr: /\b(en|de)cript(s|ing)?\b/gi,
replacement: "$1crypt$2",
reason: App.consts.reasons.spelling
},
formatting: {
expr: /\b(f)ormating\b/gi,
replacement: "$1ormatting",
reason: App.consts.reasons.spelling
},
process: {
expr: /\b(p)roces(es|ed)?\b/gi,
replacement: "$1rocess$2",
reason: App.consts.reasons.spelling
},
program: {
expr: /\b(p)rogr?amm?e?\b/gi,
replacement: "$1rogram",
reason: App.consts.reasons.spelling
},
programming: {
expr: /\b(p)rogram(ing|ed|er)\b/gi,
replacement: "$1rogramm$2",
reason: App.consts.reasons.spelling
},
programmatically: { // 40K+ https://regex101.com/r/vF2jQ8/2
expr: /\b(p)rogram+at+ica?l+y\b/gi,
replacement: "$1rogrammatically",
reason: App.consts.reasons.spelling
},
bear_with_me: {
expr: /\b(b)are (with m[ey]|it|in mind)\b/gi,
replacement: "$1ear $2",
reason: App.consts.reasons.spelling
},
weird: {
expr: /\b(w)ierd(ness|ly)\b/gi,
replacement: "$1eird$2",
reason: App.consts.reasons.spelling
},
sample: {
expr: /\b(s)maple(s|d)?\b/gi,
replacement: "$1ample$2",
reason: App.consts.reasons.spelling
},
really: { // https://regex101.com/r/sO4zD9/1
expr: /\b(r)(?:elly|ealy)\b/gi,
replacement: "$1eally",
reason: App.consts.reasons.spelling
},
finally_: {
expr: /\b(f)inall?y\b/gi,
replacement: "$1inally",
reason: App.consts.reasons.spelling
},
behaviour: { // https://regex101.com/r/rU1eB7/1
expr: /\b(b)eha?i?vi?o(r|ur|rs|urs)\b/gi,
replacement: "$1ehavio$2",
reason: App.consts.reasons.spelling
},
unfortunately: {
expr: /\b(u)nfortu?na?tly\b/gi,
replacement: "$1nfortunately",
reason: App.consts.reasons.spelling
},
whether: {
expr: /\b(w)h?eth?er\b/gi,
replacement: "$1hether",
reason: App.consts.reasons.spelling
},
whether_not_weather: { // https://regex101.com/r/oS1xE5/3
expr: /\b(w)eather(?= (?:it|we|I|or not|they|[^.?!]*(?:works?|helps?))\b)/gi,
replacement: "$1hether",
reason: App.consts.reasons.spelling
},
through: { // https://regex101.com/r/gQ0dZ1/4
expr: /\b(t)(?:hru|rough|hroug)\b/gi,
replacement: "$1hrough",
reason: App.consts.reasons.spelling
},
throughout: {
expr: /\b(t)(?:hruout|roughout)\b/gi,
replacement: "$1hroughout",
reason: App.consts.reasons.spelling
},
breakthrough: {
expr: /\b(b)reak\s+through(s)?\b/gi,
replacement: "$1reakthrough$2",
reason: App.consts.reasons.spelling
},
though: {
expr: /\b(t)(?:ho|hou|hogh)\b/gi,
replacement: "$1hough",
reason: App.consts.reasons.spelling
},
although: {
expr: /\b(a)l(?:tho|thou|thogh|tough)\b/gi,
replacement: "$1lthough",
reason: App.consts.reasons.spelling
},
thought: {
expr: /\b(t)r?ought(s)?\b/gi,
replacement: "$1hough$2",
reason: App.consts.reasons.spelling
},
throwing: {
expr: /\b(t)hroughing\b/gi, // Peter says this is "thoroughly", but a survey of SO questions indicates "throwing"
replacement: "$1hrowing",
reason: App.consts.reasons.spelling
},
a_lot: {
expr: /\b(a)lot\b/gi,
replacement: "$1 lot",
reason: App.consts.reasons.spelling
},
one_r_two_r: {
expr: /\b(refe|prefe|occu)r(ed|ing)\b/gi,
replacement: "$1rr$2",
reason: App.consts.reasons.spelling
},
occur: {
expr: /\b(o)ccure(s)?\b/gi,
replacement: "$1ccur$2",
reason: App.consts.reasons.spelling
},
preferably: {
expr: /\b(p)referrably\b/gi,
replacement: "$1referably",
reason: App.consts.reasons.spelling
},
command_line: {
expr: /\b(c)(?:omm?andline|mdline?)\b/gi,
replacement: "$1ommand-line",
reason: App.consts.reasons.spelling
},
benefits: {
expr: /\b(b)enifits\b/gi,
replacement: "$1enefits",
reason: App.consts.reasons.spelling
},
authorization: { // https://regex101.com/r/pQ8mD9/1
expr: /([^\b\w.-])(a)uth\b/gi, // This may be too ambiguous, could also mean "authentication"
replacement: "$1$2uthorization",
reason: App.consts.reasons.spelling
},
persistent: {
expr: /\b(p)ersistan(t|ce)\b/gi,
replacement: "$1ersisten$2",
reason: App.consts.reasons.spelling
},
access: { // must come before _ibility to catch accessibility with spelling variations ** but does not fix acessability?
expr: /\b(a)c+e+s+(.*)\b/gi,
replacement: "$1ccess$2",
reason: App.consts.reasons.spelling
},
_ible: {
expr: /\b(compat|incompat|access)able\b/gi,
replacement: "$1ible",
reason: App.consts.reasons.spelling
},
_ibility: {
expr: /\b(compat|incompat|access)abili?t(y|ies)\b/gi,
replacement: "$1ibilit$2",
reason: App.consts.reasons.spelling
},
separate: {
expr: /\b(s)epe?rate?(d|ly|s)?\b/gi,
replacement: "$1eparate$2",
reason: App.consts.reasons.spelling
},
separation: {
expr: /\b(s)eperation(s)?\b/gi,
replacement: "$1eparation$2",
reason: App.consts.reasons.spelling
},
definite: {
expr: /\b(d)efin(?:ate?|ite?|al|te?|et)(ly)?\b/gi, // Catches correct spelling, too.
replacement: "$1efinite$2",
reason: App.consts.reasons.spelling
},
definitive: {
expr: /\b(d)efina?tive(ly)?\b/gi,
replacement: "$1efinitive$2",
reason: App.consts.reasons.spelling
},
independent: {
expr: /\b(i)ndependant(ly)?\b/gi,
replacement: "$1ndependent$2",
reason: App.consts.reasons.spelling
},
recommend: { // https://regex101.com/r/pP9lB7/1
expr: /\b(r)ecomm?[ao]nd(ation)?\b/gi,
replacement: "$1ecommend$2",
reason: App.consts.reasons.spelling
},
compatibility: {
expr: /\b(c)ompatability\b/gi,
replacement: "$1ompatibility$2",
reason: App.consts.reasons.spelling
},
ps: {
expr: /\bps\b/g,
replacement: "PS",
reason: App.consts.reasons.spelling
},
ok: {
expr: /\bok\b/g,
replacement: "OK",
reason: App.consts.reasons.spelling
},
back_end: { // Interesting fact: backend 3x more common than back-end
expr: /\b(b)ackend\b/g,
replacement: "$1ack-end",
reason: App.consts.reasons.spelling
},
front_end: {
expr: /\b(f)rontend\b/g,
replacement: "$1ront-end",
reason: App.consts.reasons.spelling
},
data_type: {
expr: /\b(d)atatype\b/g,
replacement: "$1ata type",
reason: App.consts.reasons.spelling
},
allotted: {
expr: /\b(a)l+ot+ed\b/g,
replacement: "$1llotted",
reason: App.consts.reasons.spelling
},
straight: {
expr: /\b(s)traig?h?t\b/g,
replacement: "$1traight",
reason: App.consts.reasons.spelling
},
straightforward: {
expr: /\b(s)traig?h?t[ -]?for?ward\b/g,
replacement: "$1traightforward",
reason: App.consts.reasons.spelling
},
preceding: {
expr: /\b(p)receeding\b/gi,
replacement: "$1receding",
reason: App.consts.reasons.spelling
},
no_one: {
expr: /\b(n)o-?one\b/gi,
replacement: "$1o one",
reason: App.consts.reasons.spelling
},
de_facto: {
expr: /\b(d)e-?facto\b/gi,
replacement: "$1e facto",
reason: App.consts.reasons.spelling
},
accommodate: { // https://regex101.com/r/cL3mD9/1
expr: /\b(a)(?:c+om|com+)odate\b/gi,
replacement: "$1ccommodate",
reason: App.consts.reasons.spelling
},
kind_of: {
expr: /\b(k)inda\b/gi,
replacement: "$1ind of",
reason: App.consts.reasons.spelling
},
want_to: {
expr: /\b(w)ann?a\b/gi,
replacement: "$1ant to",
reason: App.consts.reasons.spelling
},
sort_of: {
expr: /\b(s)orta\b/gi,
replacement: "$1ort of",
reason: App.consts.reasons.spelling
},
got_to: { // https://regex101.com/r/rK6xR5/1
expr: /\b(have\s+)?(g)otta\b/gi,
replacement: "$1$2ot to",
reason: App.consts.reasons.spelling
},
dont_know: { // https://regex101.com/r/rK6xR5/1
expr: /\b(d)[uo]nn?o\b/gi,
replacement: "$1on't know",
reason: App.consts.reasons.spelling
},
going_to: {
expr: /\b(g)[ou]nn?a\b/gi,
replacement: "$1oing to",
reason: App.consts.reasons.spelling
},
crashes: {
expr: /\b(c)rashs\b/gi,
replacement: "$1rashes",
reason: App.consts.reasons.spelling
},
pattern: {
expr: /\b(p)at?(?:trn|tren|tern)(s)?\b/gi,
replacement: "$1attern$2",
reason: App.consts.reasons.spelling
},
syntax: {
expr: /\b(s)[yi]nt[ae]?x\b/gi,
replacement: "$1yntax",
reason: App.consts.reasons.spelling
},
correct: {
expr: /\b(c)orr?ec[ty]/gi, // No \b at end, to include correction, correcting, corrected
replacement: "$1orrect",
reason: App.consts.reasons.spelling
},
correctly: {
expr: /\b(c)orr?ec(?:lt?|t?l)y\b/ig,
replacement: "$1orrectly",
reason: App.consts.reasons.spelling
},
integer: {
expr: /\b(i)nte?r?ger(s)?\b/gi,
replacement: "$1nteger$2",
reason: App.consts.reasons.spelling
},
several: {
expr: /\b(s)er?v[ea]?r[ae]?l\b/gi,
replacement: "$1everal",
reason: App.consts.reasons.spelling
},
solution: {
expr: /\b(s)ou?lu?ti?on\b/gi,
replacement: "$1olution",
reason: App.consts.reasons.spelling
},
somebody: {
expr: /\b(s)ombody\b/gi,
replacement: "$1omebody",
reason: App.consts.reasons.spelling
},
everything: {
expr: /\b(e)ve?r[yi]?thing\b/gi,
replacement: "$1verything",
reason: App.consts.reasons.spelling
},
button: {
expr: /\b(b)[uo]+tt?[ou]n\b/gi,
replacement: "$1utton",
reason: App.consts.reasons.spelling
},
before: {
expr: /\b(b)e?fo?re?\b/gi,
replacement: "$1efore",
reason: App.consts.reasons.spelling
},
example: { // https://regex101.com/r/uU4bH5/2
expr: /\b(e)(?:xsample|xamle|x?amp[le]{1,2}|xemple|xaple)(s)?\b/gi,
replacement: "$1xample$2",
reason: App.consts.reasons.spelling
},
somewhere: { // https://regex101.com/r/aU2nP5/1
expr: /\b(s)ome?(?: ?where?|w[ea]+re?)\b/gi,
replacement: "$1omewhere",
reason: App.consts.reasons.spelling
},
with: { // https://regex101.com/r/xO5dP3/2
expr: /\b(w)(?:hith|iht)(?=(ou?t|in)?\b)/gi,
replacement: "$1ith",
reason: App.consts.reasons.spelling
},
without: { // After 'with' rule, only need to check 'out'
expr: /\b(w)ithou?t\b/gi,
replacement: "$1ithout",
reason: App.consts.reasons.spelling
},
reproducible: {
expr: /\b(r)eproduct?[ia]ble\b/gi,
replacement: "$1eproducible",
reason: App.consts.reasons.spelling
},
unnecessary: {
expr: /\b(u)nn?ecc?ess?ary\b/gi,
replacement: "$1nnecessary",
reason: App.consts.reasons.spelling
},
require: { // https://regex101.com/r/nS6kM5/1
expr: /\b(r)equie?re?(d|s|me?nts?)?\b/gi,
replacement: "$1equire$2",
reason: App.consts.reasons.spelling
},
address: {
expr: /\b(a)dd?ress?(es|ed|ing)?e?\b/gi,
replacement: "$1ddress$2",
reason: App.consts.reasons.spelling
},
password: {
expr: /\b(p)ass?wo?rd?(s)?\b/gi,
replacement: "$1assword$2",
reason: App.consts.reasons.spelling
},
method: {
expr: /\b(m)e[th]+[oeu]+d(s)?\b/gi,
replacement: "$1ethod$2",
reason: App.consts.reasons.spelling
},
property: {
expr: /\b(p)rope?rt[iey]?\b/gi,
replacement: "$1roperty",
reason: App.consts.reasons.spelling
},
properties: {
expr: /\b(p)rope?rt[iey]+s\b/gi,
replacement: "$1roperties",
reason: App.consts.reasons.spelling
},
wireless: {
expr: /\b(w)ire?le?ss?\b/gi,
replacement: "$1ireless",
reason: App.consts.reasons.spelling
},
possible: {
expr: /\b(p)oss?[ai]?ble\b/gi,
replacement: "$1ossible",
reason: App.consts.reasons.spelling
},
fields_yields: { // https://regex101.com/r/cJ8rM4/1
expr: /\b(f|y)(?:ei?|ie?)l?d(s|ing|ed)?\b/gi,
replacement: "$1ield$2",
reason: App.consts.reasons.spelling
},
execute: {
expr: /\b(e)x[ei]?cute(s|d)\b/gi,
replacement: "$1xecute$2",
reason: App.consts.reasons.spelling
},
algorithm: {
expr: /\b(a)lgo?r[iy]?th?[iya]?m(s)?\b/gi,
replacement: "$1lgorithm$2",
reason: App.consts.reasons.spelling
},
version: { // https://regex101.com/r/wE8uD0/1
expr: /\b(v)er(?:s[io]*|io)n(s|ing|ed)?\b/gi,
replacement: "$1ersion$2",
reason: App.consts.reasons.spelling
},
which: { // 22,772 of these as of 12-Nov-2015!
expr: /\b(w)(?:ich|hic)\b/gi,
replacement: "$1hich",
reason: App.consts.reasons.spelling
},
disappear: {
expr: /\b(d)is?apea?r(ing|ed|s)?\b/gi,
replacement: "$1isappear$2",
reason: App.consts.reasons.spelling
},
because: {
expr: /\b(b)ec[ao]u?se?\b/gi,
replacement: "$1ecause",
reason: App.consts.reasons.spelling
},
should: {
expr: /\b(s)(?:hold|houd|huld|hud|ould)\b/gi,
replacement: "$1hould",
reason: App.consts.reasons.spelling
},
totally: {
expr: /\b(t)ota?ll?y\b/gi,
replacement: "$1otally",
reason: App.consts.reasons.spelling
},
lambda: {
expr: /\b(l)am[bd]+a\b/gi,
replacement: "$1ambda",
reason: App.consts.reasons.spelling
},
command: {
expr: /\b(c)om(?:m?ad|and|mnd)(ed|s|ing|ers?|o)?\b/gi,
replacement: "$1ommand$2",
reason: App.consts.reasons.spelling
},
therefore: {
expr: /\b(t)here?fore?\b/gi,
replacement: "$1herefore",
reason: App.consts.reasons.spelling
},
parameter: {
expr: /\b(p)ara?m[ea]n?ter(s)?\b/gi,
replacement: "$1arameter$2",
reason: App.consts.reasons.spelling
},
just: {
expr: /\b(j)(?:uste|us)\b/gi,
replacement: "$1ust",
reason: App.consts.reasons.spelling
},
fulfill: {
expr: /\b(f)ull?\s?fill\b/gi,
replacement: "$1ulfill",
reason: App.consts.reasons.spelling
},
coming: {
expr: /\b(c)omming\b/gi,
replacement: "$1oming",
reason: App.consts.reasons.spelling
},
tried: { // 8,540 of these!
expr: /\b(t)rye(d|s)\b/gi,
replacement: "$1rie$2",
reason: App.consts.reasons.spelling
},
basically: { // 7,924 of these!
expr: /\b(b)asica?l+y\b/gi,
replacement: "$1asically",
reason: App.consts.reasons.spelling
},
completely: { // 4,793 examples! https://regex101.com/r/oG7nH6/2
expr: /\b(c)ompl?ete?l?e?y\b/gi,
replacement: "$1ompletely",
reason: App.consts.reasons.spelling
},
misread: {
expr: /\b(m)is+[ -]?rea?d\b/gi,
replacement: "$1isread",
reason: App.consts.reasons.spelling
},
database: {
expr: /\b(d)atabaes?\b/gi,
replacement: "$1atabase",
reason: App.consts.reasons.spelling
},
output: { // https://regex101.com/r/bP9kY2/1
expr: /\b(o)ut ?put+(?:ed)?\b/gi,
replacement: "$1utput",
reason: App.consts.reasons.spelling
},
useful: { // 11,542 "usefull"
expr: /\b(u)se(?:full| ful)\b/gi,
replacement: "$1seful",
reason: App.consts.reasons.spelling
},
classes: {
expr: /\b(c)la(se|ss)s\b/gi,
replacement: "$1lasses",
reason: App.consts.reasons.spelling
},
english: {
expr: /\benglisc?h?\b/gi,
replacement: "English",
reason: App.consts.reasons.spelling
},
inheritance: { // 1700 x inheritence
expr: /\b(i)nherit[ae]n[cs]e?\b/gi,
replacement: "$1nheritance",
reason: App.consts.reasons.spelling
},
advice: { // 9000 x advices
expr: /\b(a)dvices\b/gi,
replacement: "$1dvice",
reason: App.consts.reasons.spelling
},
when: {
expr: /\b(w)h[ea]ne?\b/gi,
replacement: "$1hen",
reason: App.consts.reasons.spelling
},
and_then: { // 16K instances of this!
expr: /\b(a)nd,? tha?n\b/gi,
replacement: "$1nd then",
reason: App.consts.reasons.spelling
},
un_initialize: { // >4K instances https://regex101.com/r/lY2hY1/1
//Should not change from/to British <-> American English.
expr: /\b((?:un-?|re-?)?i)n?i?t[ia]+li?([zs])(e|ed|[eo]r|es|ing)\b/gi,
replacement: function(match, prefix, engAmer, suffix) {
return (prefix+'nitiali' + engAmer + suffix).replace("-","");
},
reason: App.consts.reasons.spelling
},
character: { // 3500+ instances, https://regex101.com/r/lG1qH0/1
expr: /\b(c)(?:har|h?arac?h?ter)(s|istics?|i[zs]e)?\b/gi,
replacement: "$1haracter$2",
reason: App.consts.reasons.spelling
},
found: {
expr: /\b(f)inded\b/gi,
replacement: "$1ound",
reason: App.consts.reasons.spelling
},
tuple: { // https://regex101.com/r/zP7zM2/1
expr: /\b(t)o?up+e?le?(s)?\b/gi,
replacement: "$1uple$2",
reason: App.consts.reasons.spelling
},
i_read: {
expr: /\b(I|I've|we|they) red\b/gi,
replacement: "$1 read",
reason: App.consts.reasons.spelling
},
customize: { // http://grammarist.com/spelling/customise-customize/ Don't change AME/BRE usage.
expr: /\b(c)u[st]+[oui]mi([zs])(e)?/gi,
replacement: "$1ustomi$2$3",
reason: App.consts.reasons.spelling
},
customizable: { // Common errors are to retain 'e', and/or to use ible, not able
expr: /\b(c)ustomiz[ea]+(tions?|ble|bility|bilities)/gi,
replacement: "$1ustomiza$2",
reason: App.consts.reasons.spelling
},
across: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\b(a)c+ros+\b/gi,
replacement: "$1cross",
reason: App.consts.reasons.spelling
},
immediate: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\b(i)m+ed[ia]+te?l?(ly)?\b/gi,
replacement: "$1mmediate$2",
reason: App.consts.reasons.spelling
},
every_time: { // https://regex101.com/r/dB6jC2/1
expr: /\b(e)v[ery]+time?\b/gi,
replacement: "$1very time",
reason: App.consts.reasons.spelling
},
achieve: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/bZ2qJ1/1
expr: /\b(a)ch[ei]+ve?(s|d|ment)?\b/gi,
replacement: "$1chieve$2",
reason: App.consts.reasons.spelling
},
apparent: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/dO3aH4/2
expr: /\b(a)p+ar[ae]nt?(ly)?\b/gi,
replacement: "$1pparent$2",
reason: App.consts.reasons.spelling
},
appear: { // https://regex101.com/r/oL8lI1/1
expr: /\b(a)p+[ea]+re?(s|ed|ing)?\b/gi,
replacement: "$1ppear$2",
reason: App.consts.reasons.spelling
},
appearance: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/eP2bF9/1
expr: /\b(a)p+[ea]+r[ea]+nce(s)?\b/gi,
replacement: "$1ppearance$2",
reason: App.consts.reasons.spelling
},
beginning: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/sT4gQ0/2
expr: /\b(b)egi?n+in?g/gi,
replacement: "$1eginning",
reason: App.consts.reasons.spelling
},
believe: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/pM1cC6/1
expr: /\b(b)e?l[ei]+v(e|ing|able)/gi, // Note lack of \b at end.
replacement: "$1eliev$2",
reason: App.consts.reasons.spelling
},
colleague: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/xN8qD9/1
expr: /\b(c)ol+[ea]+gue(s)?\b/gi,
replacement: "$1olleague$2",
reason: App.consts.reasons.spelling
},
implement: { // https://regex101.com/r/zW1aS5/1
expr: /\b(i)mpl?[ei]?ment/gi,
replacement: "$1mplement",
reason: App.consts.reasons.spelling
},
simultaneous: { // https://regex101.com/r/iB0mE7/1
expr: /\b(s)imu[lt]+an[ieou]+se?/gi,
replacement: "$1imultaneous",
reason: App.consts.reasons.spelling
},
environment: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/qD5zU6/1
expr: /\b(e)nvi?ro?[nmt]+ent/gi,
replacement: "$1nvironment",
reason: App.consts.reasons.spelling
},
existence: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/mH7hA6/1
expr: /\b(e)xist[ae]n[cs]e/gi,
replacement: "$1xistence",
reason: App.consts.reasons.spelling
},
further: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/sE6nY3/1
expr: /\b(f)(?:u|[au]r)th?er/gi,
replacement: "$1urther",
reason: App.consts.reasons.spelling
},
jist: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\bjist of\b/gi,
replacement: "gist of",
reason: App.consts.reasons.spelling
},
noticeable: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\b(n)oticabl(e|y)\b/gi,
replacement: "$1oticeabl$2",
reason: App.consts.reasons.spelling
},
publicly: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\b(p)ublica?l*y\b/gi,
replacement: "$1ublicly",
reason: App.consts.reasons.spelling
},
receive: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\b(r)ec[ie]+v(e[rds]?|ing)/gi,
replacement: "$1eceiv$2",
reason: App.consts.reasons.spelling
},
referred: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/kE0oZ5/5
expr: /\b(r)efer(?!s|enc\w*|r\w*)(?=\w)/gi,
replacement: "$1eferr",
reason: App.consts.reasons.spelling
},
remember: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\b(r)e(?:mber|meber|memer)/gi,
replacement: "$1emember",
reason: App.consts.reasons.spelling
},
sense: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\b(s)ence/gi,
replacement: "$1ense",
reason: App.consts.reasons.spelling
},
supersede: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/mA5nC1/1
expr: /(s)uperced(e[sd]?|ing)\b/gi,
replacement: "$1upersed$2",
reason: App.consts.reasons.spelling
},
surprise: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/uS8oS4/1
expr: /\b(s)ur?pri[scz](e[ds]?|ing(?:ly)?)\b/gi,
replacement: "$1urpris$2",
reason: App.consts.reasons.spelling
},
connection: { // https://regex101.com/r/rO2wH0/1
expr: /\b(c)on+e[ctx]+i?on(s)?/gi,
replacement: "$1onnection$2",
reason: App.consts.reasons.spelling
},
additional: { // https://regex101.com/r/iM4xV5/2
expr: /\b(a)d+i.?tio?n[al]+?(ly)?\b/gi,
replacement: "$1dditional$2",
reason: App.consts.reasons.spelling
},
automatic: { // https://regex101.com/r/fU2hF1/3
expr: /\b(a)(?:uto[ma]+[tic]+|tomatic)(?!e|[io]+[nr])/gi,
replacement: "$1utomatic",
reason: App.consts.reasons.spelling
},
automatically: { // 6K+
expr: /\b(a)utomatic[aly]+\b/gi,
replacement: "$1utomatically",
reason: App.consts.reasons.spelling
},
running: { // 2K+
expr: /\b(r)un+in?g\b/gi,
replacement: "$1unning",
reason: App.consts.reasons.spelling
},
even_though: { // 2.7K+
expr: /\b(e)venth?ou?[gh]+\b/gi,
replacement: "$1ven though",
reason: App.consts.reasons.spelling
},
tomorrow: { // http://www.oxforddictionaries.com/words/common-misspellings
expr: /\b(t)om+or+ow\b/gi,
replacement: "$1omorrow",
reason: App.consts.reasons.spelling
},
truly: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/yV4rZ9/1
expr: /\b(t)rue?l+e?y\b/gi,
replacement: "$1ruly",
reason: App.consts.reasons.spelling
},
until: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/tK8rV5/2
expr: /\b(?:(u)nti?l+|(t)il+)\b/gi,
replacement: function (match,f1,f2) {
var fchar = f1||f2;
return ((fchar.toUpperCase() === fchar) ? "U" : "u") + "ntil";
},
reason: App.consts.reasons.spelling
},
where: { // Must precede "wherever"
expr: /\b(w)her\b/gi,
replacement: "$1here",
reason: App.consts.reasons.spelling
},
wherever: { // http://www.oxforddictionaries.com/words/common-misspellings https://regex101.com/r/iJ4bG1/1
expr: /\b(w)here ?ever\b/gi,
replacement: "$1herever",
reason: App.consts.reasons.spelling
},
reset: {
expr: /\b(r)eset+ed\b/gi,
replacement: "$1eset",
reason: App.consts.reasons.spelling
},
begin: { // https://regex101.com/r/xZ9iC3/1
expr: /\b(b)eg+in?(ning|ner)?\b/gi,
replacement: "$1egin$2",
reason: App.consts.reasons.spelling
},
update: { // https://regex101.com/r/rF6fZ2/1
expr: /\b(u)[pd]+at(e|ed|er|es|ing)\b/gi,
replacement: "$1pdat$2",
reason: App.consts.reasons.spelling
},
question: { // https://regex101.com/r/tC5yN8/2
expr: /\b(q)[ues]+t[io]+ne?/gi,
replacement: "$1uestion",
reason: App.consts.reasons.spelling
},
variable: { // hhttps://regex101.com/r/sI3lT5/1
//thanks Kyll - http://chat.stackoverflow.com/transcript/message/29352137#29352137
expr: /\b(v)[ai]+r[ia]+b[le]+(s)?\b/gi,
replacement: "$1ariable$2",
reason: App.consts.reasons.spelling
},
function_: { // https://regex101.com/r/kJu78M/1 Old regex101 URL was to RegExp for "variable"
//thanks Kyll - http://chat.stackoverflow.com/transcript/message/29352203#29352203
expr: /\b(f)(?:[un]+ct[io]+n*|u[ncti]+onn?)/gi,
replacement: "$1unction",
reason: App.consts.reasons.spelling
},
being: { // 4,600+
expr: /\b(b)eeing\b/gi,
replacement: "$1eing",
reason: App.consts.reasons.spelling
},
happen: { // https://regex101.com/r/jH8rE5/2
// thanks Praveen - http://chat.stackoverflow.com/transcript/message/29427717#29427717
expr: /\b(h)ap+e?n(e?d|s|ing)?\b/gi,
replacement: function (match,fChar,suffix) {
suffix = suffix || '';
return fChar+"appen"+suffix.replace(/^d/,'ed');
},
reason: App.consts.reasons.spelling
},
actual: { // https://regex101.com/r/mT1cL7/2
expr: /\b(a)(?:c+t{0,1}[ua]+|[ct]ua)l*(ly)?\b/gi,
replacement: "$1ctual$2",
reason: App.consts.reasons.spelling
},
assign: { // https://regex101.com/r/cM7mF2/1
expr: /\b(a)s+i[gn]+/gi,
replacement: "$1ssign",
reason: App.consts.reasons.spelling
},
prefer_refer: { // https://regex101.com/r/gG7bQ9/1
expr: /\b([pr]+)ef+e?r+([ea]nc|able)/gi,
replacement: function(match,fChar,suffix) {
return fChar+"efer"+suffix.replace(/anc/,"enc");
},
reason: App.consts.reasons.spelling
},
use_case: { // 4,556 (+818 usecases)
expr: /\b(u)se(c)ase/gi,
replacement: "$1se $2ase",
reason: App.consts.reasons.spelling
},
matches: { //
expr: /\b(m)atc[he]s/gi,
replacement: "$1atches",
reason: App.consts.reasons.spelling
},
specific: { //
expr: /\b(s)pe[cs]i?fic/gi,
replacement: "$1pecific",
reason: App.consts.reasons.spelling
},
computer: { // https://regex101.com/r/kJ3iY8/2
expr: /\b(c)o?m?p[ue]?t?[eoa]r(s)?\b/gi,
replacement: "$1omputer",
reason: App.consts.reasons.spelling
},
something_like: { // Some thing like -- 6,468 posts
expr: /\b(s)ome thing like/gi,
replacement: "$1omething like",
reason: App.consts.reasons.spelling
},
maybe_something: { // May be something -- 4,259 posts
expr: /\b(m)ay be some ?thing/gi,
replacement: "$1aybe something",
reason: App.consts.reasons.spelling
},
targeting: { // 3,151 posts
expr: /\b(t)argetting/gi,
replacement: "$1argeting",
reason: App.consts.reasons.spelling
},
column: { // 1,363 posts
expr: /\b(c)olou?mn?(s)?/gi,
replacement: "$1olumn$2",
reason: App.consts.reasons.spelling
},
array: {
expr: /\b(a)(?:rry|ray)(s)?/gi,
replacement: "$1rray$2",
reason: App.consts.reasons.spelling
},
suggest: { // https://regex101.com/r/mH1fY7/1
expr: /\b(s)ugest/gi,
replacement: "$1uggest",
reason: App.consts.reasons.spelling
},
synchronize: { // subset of https://regex101.com/r/vG6jQ8/1
expr: /(s)[yi]nch?ron/gi,
replacement: "$1ynchron",
reason: App.consts.reasons.spelling
},
synchronous: {
expr: /(s)ynchron[ou]+s/gi,
replacement: "$1ynchronous",
reason: App.consts.reasons.spelling
},
exception: { // https://regex101.com/r/jK4gX6/1
expr: /\b(e)[xc]+e[pt]+ion/gi,
replacement: "$1xception",
reason: App.consts.reasons.spelling
},
information: { // https://regex101.com/r/yE3fD6/1
expr: /\b(i)nfo[rm]+at[io]+ns?\b/gi,
replacement: "$1nformation",
reason: App.consts.reasons.spelling
},
piece: {
expr: /\b(p)eice(s|d)?\b/gi,
replacement: "$1iece$2",
reason: App.consts.reasons.spelling
},
peaceToPiece: { // https://regex101.com/r/tZ1fY3/1
expr: /\b(p)eace(s)?(?= of [\w -]*(?:code|cake|script|text|string|content|image|file))/gi,
replacement: "$1iece$2",
reason: App.consts.reasons.spelling
},
is_there_a: { // 2K+ posts
expr: /\b(i)s their a\b/gi,
replacement: "$1s there a",
reason: App.consts.reasons.spelling
},
usage: {
expr: /\b(u)s[ea]+ge?\b/gi,
replacement: "$1sage",
reason: App.consts.reasons.spelling
},
background: { // 1,583+ posts
expr: /\b(b)a[ck]+ ?gr[ou]+[nd]+(s?)s*\b/gi,
replacement: "$1ackground$2",
reason: App.consts.reasons.spelling
},
preempt: {
expr: /\b(p)r[e -]+m[pt]+/gi,
replacement: "$1reempt",
reason: App.consts.reasons.spelling
},
extension: {
expr: /\b(e)xten[st]ion(s?)s*\b/gi,
replacement: "$1xtension$2",
reason: App.consts.reasons.spelling
},
addon: {
expr: /\b(a)ddon(s?)s*\b/gi,
replacement: "$1dd-on$2",
reason: App.consts.reasons.spelling
},
addonsdk: {
expr: /\b(a)ddon-?sdk\b/gi,
replacement: "$1dd-on SDK",
reason: App.consts.reasons.spelling
},
thankful: {
expr: /\b(t)hankfull?\b/gi,
replacement: "$1hankful",
reason: App.consts.reasons.spelling
},
know: {
expr: /\b(k)now?\b/gi,
replacement: "$1now",
reason: App.consts.reasons.spelling
},
/*
** Grammar - Correct common grammatical errors.
**/
start_with_so: { // https://regex101.com/r/gP1xA2/2
expr: /^(?:okay\b|ok\b|so\b|[ \t,-])+/gi,
replacement: "",
reason: App.consts.reasons.grammar
},
protect_column_a_Begin: { // Prevent "Column A" from being changed (Begin); order in App.edits Object does not matter.
expr: /(column\s+)(An?)\b/gi,
replacement: "$1_xPlacexHolderxColumn$2PlacexHolderx_",
notAlone: true, // Don't run unless it's as part of another edit rule.
reason: App.consts.reasons.silent
},
a_vs_an: { // See http://stackoverflow.com/q/34440307/1677912
expr: /\b(a|an) ([\(\"'“‘`<-]*\w*)\b/gim, // https://regex101.com/r/nE1yA4/5
replacement: function( match, article, following ) {
var input = following.replace(/^[\s\(\"'“‘`<-]+|\s+$/g, "");//strip initial punctuation symbols
var res = AvsAnOverride_(input) || AvsAnSimple.query(input); // eslint-disable-line no-use-before-define
var newArticle = article[0] + res.substr(1); // Preserve existing capitalization
return newArticle+' '+following;
// Hack alert: Due to the technical nature of SO subjects, many common terms
// are not well-represented in the data used by AvsAnSimple, so we need to
// provide a way to override it.
// NOTE: AvsAnSimple is susceptible to unicode mess-up; if you suddenly see many
// words starting with vowels being incorrectly treated, check that the script
// has not had a unicode substitution error. (Git did this do me, once.)
function AvsAnOverride_(fword) {
var exceptionsA_ = /^(?:uis?)/i;
var exceptionsAn_ = /^(?:[lr]value|a\b|sql|ns|ng|is)/i;
return (exceptionsA_.test(fword) ? article[0] :
exceptionsAn_.test(fword) ? article[0]+"n" : false);
}
},
runBefore: ['protect_column_a_Begin'],
runAfter: ['protect_column_a_End'],
reason: App.consts.reasons.grammar
},
protect_column_a_End: { // Prevent "Column A" from being changed (End); order in App.edits Object does not matter.
expr: /_xPlacexHolderxColumn(An?)PlacexHolderx_/g,
replacement: "$1",
notAlone: true, // Don't run unless it's as part of another edit rule.
reason: App.consts.reasons.silent
},
firstcaps: {
// https://regex101.com/r/JnSYVw/1
// Regex finds all sentences; replacement must determine whether it needs to capitalize.
expr: /(([A-Za-z]|\d(?!\d*\. )|[.$_]\w+)(\S*))((?:(?:etc\.|i\.e\.|e\.g\.|vs\.|\.\.\.|\w*\.(?![\s")])|[*-]+|\n(?![ \t]*\n| *(?:[*-]|\d+\.))|[^.?!\n]?))+(?:([.?!]+)(?=[\s")]|$)|\n\n|\n(?= *[*-])|\n(?= *\d+\.)|$))/gi,
replacement: function(sentence, fWord, fChar, fWordPost, sentencePost/*, endpunc*/) {
var capChar = fChar.toUpperCase();
if (sentence === "undefined" || capChar === fChar) return sentence; // MUST match sentence, or gets counted as a change.
if (!fWord) fWord = '';
var fWordChars = fWord.split('');
// Leave some words alone: filenames, camelCase
for (var i=0; i ])\2{1,}/g,
replacement: "$1$2",
reason: App.consts.reasons.grammar
},
i_want: { //https://regex101.com/r/iD2tU0/5
expr: /\b(?:are )?(I|you|they) ?(?:['a ]*m|are)? want(?:ing|s)?\b/gi,
replacement: "$1 want",
rerun: ["firstcaps"],
reason: App.consts.reasons.grammar
},
oxford_comma: { // https://regex101.com/r/xN0mF6/6
expr: /((?:[\w'-]+,\s+)+(?:[\w'-]+\s){0,2}[\w'-]+)(\s+(and|or)\s+[\w'-]+)/g,
replacement: "$1,$2",
reason: App.consts.reasons.grammar
},
i_have_find: {
expr: /\b(I|you) have find\b(?![(]|\.\w)/gi,
replacement: "$1 have found",
reason: App.consts.reasons.grammar
},
let_s_say: { // 60K!
expr: /\b(l)ets (say|see|look|just|put|have|leave|give|write)\b/gi,
replacement: "$1et's $2",
reason: App.consts.reasons.grammar
},
suggest_me: { // 36K
expr: /\b(s)u[gj]+est(s)? me/gi,
replacement: "$1uggest$2",
reason: App.consts.reasons.grammar
},
perfectly: { // 36K
expr: /\b(p)[re]+fectly/gi,
replacement: "$1erfectly",
reason: App.consts.reasons.grammar
},
works_perfectly: { // 13K+ posts
expr: /\b(w)ork(s)? p[er]+fect\b/gi,
replacement: "$1ork$2 perfectly",
reason: App.consts.reasons.grammar
},
doesnt_work: { // 900+ posts
expr: /\b(d)on't works/gi,
replacement: "$1oesn't work",
reason: App.consts.reasons.grammar
},
how_it_works: { // 38,563+ posts
expr: /\b(h)ow it works\?/gi,
replacement: "$1ow does it work?",
reason: App.consts.reasons.grammar
},
double_period: { // https://regex101.com/r/fG6lY3/1
expr: /([^.]|^)\.{2}(?!\.)/g,
replacement: "$1.",
reason: App.consts.reasons.grammar
},
/*
** "Five exclamation marks, the sure sign of an insane mind"
**/
pysanky: {
expr: /([^\!])[!]{5}(?!\!)/g,
replacement: "$1!",
reason: window.atob('IkZpdmUgZXhjbGFtYXRpb24gbWFya3MsIHRoZSBzdXJlIHNpZ24gb2YgYW4gaW5zYW5lIG1pbmQi')
},
/*
** Noise reduction - Remove fluff that adds nothing of technical value to posts.
**/
help: {
expr: /\b(h)(?:[ea]l?p)(?![-])\b/gi,
replacement: "$1elp",
reason: App.consts.reasons.silent
},
thank: { // https://regex101.com/r/pN0sX4/2
expr: /\b(t)(?:[hank]{2,4}|hx)(?= *(you\b))\b/gi,
replacement: "$1hank",
reason: App.consts.reasons.silent
},
thanks: { // https://regex101.com/r/cO7gG2/2
expr: /\b(t)(?:anks *(?=[.?!]\n|to|for|in|ever)|[han]{3}([ks]{2}|x)+|hx|anx)\b/gi,
replacement: "$1hanks",
reason: App.consts.reasons.silent
},
please: {
expr: /\b(p)(?:lz+|lse?|l?ease?)\b/gi,
replacement: "$1lease",
reason: App.consts.reasons.silent
},
tia: { // common acronym; should only remove "thanks in advance" at end of post
expr: /\btia$/gi,
replacement: "",
reason: App.consts.reasons.noise
},
editupdate: {
// https://regex101.com/r/tT2pK6/9
expr: /([-_*]+[\t ]*\b(edit|update)\b([\t ]*#?[0-9]+)?[\t ]*:*[\t ]*[-_*]+:*|[\t ]*\b(edit|update)\b([\t ]*#?[0-9]+)?\s*:+[\t ]*)/gi,
replacement: "",
reason: App.consts.reasons.noise
},
complimentaryClose: { // https://regex101.com/r/hL3kT5/7
expr: /^\s*(?:(?:kind(?:est)* |best )*regards?|cheers?|greetings?|thanks|thank you|peace)\b,?(?:[^_\r\n]|_(?:[^x\r\n]|$))*(?: *[\r\n]){0,2}(?:[^_\r\n]|_(?:[^x\r\n]|$))*(?:[.!?: ]*|$)/gim,
replacement: "",
reason: App.consts.reasons.noise
},
// http://meta.stackexchange.com/questions/2950/should-hi-thanks-taglines-and-salutations-be-removed-from-posts/93989#93989
salutation: { // https://regex101.com/r/yS9lN8/11
expr: /^\s*(?:dears?\b.*$|greetings?\b.*$|(?:hi(?:ya)*|hel+o+|heya?|hai|g'?day|peace[^.!]*|good\s?(?:evening|morning|day|afternoon)|ahoy|folks|guys)[,\s]*(?:\s+(?:you|all|guys|folks|friends?|there|everyone|people|matey?s?|bud+(y|ies))*))(?:[,.!?: ]*|$)/gmi,
replacement: "",
reason: App.consts.reasons.noise
},
badphrases: { // https://regex101.com/r/gE2hH6/18
expr: /[^\n.!?:]*(?:thanks|thank[ -]you|please|help|suggest(?:ions))\b(?:[ .?!]*$|[^\n.!?:]*\b(?:help|ap+reciat\w*|me|advan\w*|a ?lot|beforehand)\b[^\n.!?:]*)[.!?_*]*(?!xPlacexHolder)/gim,
replacement: "",
reason: App.consts.reasons.noise
},
imnew: {
expr: /(?! )[\w\s]*\bi[' ]?a?m +(?:kinda|really) *new\w* +(?:to|in) *\w* *(?:and|[;,.!?])? */gi,
replacement: "",
reason: App.consts.reasons.noise
},
sorry4english: { // https://regex101.com/r/pG3oD6/8
expr: /[^\n.!?]*((sorry|ap+olog.*|forgive)\b[^.!?:\n\r]+\b((bad|my|poor) english)|(english[^.!?:\n\r]+)\b(tongue|language))\b[^.!?:\n\r]*(?:[.!?:_*])*/gi,
replacement: "",
reason: App.consts.reasons.noise
},
hope_this_helps: { // https://regex101.com/r/yF1uY0/1
expr: /^\s*i? ?\bhope\b[^\n.!?:]*helps?[^\n.!?:]*[,.!?: ()^-]*$/gmi,
replacement: "",
reason: App.consts.reasons.noise
},
enter_code_here: {
expr: /\benter (?:code|image description|link description) here\b/gi,
replacement: "",
reason: App.consts.reasons.noise
},
i_have_a_question: { // https://regex101.com/r/uM0nQ1/1
expr: /^(?:I have|I've)(?: got)* a question[ \t,.?:-]*(?:about|when)?[ \t,.?:-]*/gi,
replacement: "",
reason: App.consts.reasons.noise
},
no_rep_to_comment: { // https://regex101.com/r/vL2uI0/3
expr: /(?:[^\n.!?:]*(?:rep|reputation)\b[^.!?:\n\r]+\bcomment(?:[.!?:\n\r)]+|[^.!?:\n\r]*?(?:\bbut\b|[, ]*so|[.,)]+)))/gi,
replacement: "",
reason: App.consts.reasons.noise
},
/*
** Layout - Minimize whitespace (which is compressed by markup).
** Must follow noise reduction.
** Leading and trailing spaces are part of Markdown formatting; leave them.
**/
space_then_symbol: { // https://regex101.com/r/fN6lL7/6
expr: /([^ \n\r\[\)])(\((?!\)))/gm,
replacement: "$1 $2",
debug: false,
reason: App.consts.reasons.layout
},
no_space_before_symbol: { // https://regex101.com/r/qB9lS0/2
expr: /(?:(^ +)|[ ]+?([,?!:)]+|[.]+(?![\S])))/gm,
replacement: "$1$2",
debug: false,
reason: App.consts.reasons.layout
},
symbol_then_space: { // https://regex101.com/r/iD9aS1/6
expr: /(?:\b)([,?!:)]+|[.]{3})(?:\b)(?![\d])/gm,
replacement: "$1 ",
debug: false,
reason: App.consts.reasons.layout
},
space_symbol_space: {
expr: /(?:\b| +)([&])(?: |\b)(?![\d])/g,
replacement: " $1 ",
debug: false,
reason: App.consts.reasons.layout
},
multiplespaces: { // https://regex101.com/r/hY9hQ3/3
expr: /(?!^)[ ]{2,}(?! ?$)/gm,
replacement: " ",
debug: false,
reason: App.consts.reasons.layout
},
numbered_list: { // https://regex101.com/r/mI1aV3/3
expr: /([\n\r]|^)+\(?([ \t]*[\d]+)[).:-] */gm,
replacement: "$1$1$2. ",
reason: App.consts.reasons.layout
},
no_html_break: { // https://regex101.com/r/xP2oW9/4
expr: / *< *br *\/? *> */gi,
replacement: " ",
reason: App.consts.reasons.layout
},
// DISABLED temporarily - see Issue #115
//blanklines: { // https://regex101.com/r/eA5hA2/2
// expr: /^(?: *[\n\r\f])+|(?: *[\n\r\f])+$|((?: *[\n\r\f]){2})(?:(?: *[\n\r\f]))+/g,
// replacement: "$1",
// debug: false,
// reason: App.consts.reasons.layout
//},
//mdash and ndash
// See: https://regex101.com/r/vnM5cO/1 for text which was tested.
// --- is converted to —
// -- is converted to –
// Not having spaces is enforced around the — (For those — which are added here).
// Spaces are enforced around the – (For those – which are added here).
mdash: { //Must follow layout changes, due to adding an HTML tag, which would be mangled as a result of layout substitutions.
expr: /([^-]|^)---([^-]|$)/gmi,
replacement: "$1—$2",
reason: App.consts.reasons.grammar,
runAfter: [
'mdash_clear',
'mdash_clear' //Yes, twice.
]
},
mdash_clear: { //For an mdash that we've added, make it so there's no space.
expr: /(?: *—(?! *$) +| +—)/gmi,
//expr: /(?: +—(?! +$) +| +—|—(?! +$) +)/gmi,
replacement: "—",
reason: App.consts.reasons.silent,
notAlone: true
},
ndash: { //Must follow layout changes, due to adding an HTML tag, which would be mangled as a result of layout substitutions.
expr: /([^-]|^)--([^-]|$)/gmi,
replacement: "$1–$2",
reason: App.consts.reasons.grammar,
runAfter: [ //These are run in the order listed.
'ndash_protect_start',
'ndash_clear_left',
'ndash_unprotect_start',
'ndash_clear_right'
]
},
ndash_protect_start: { //For an ndash that we've added at the start of the line, make it so we don't add a space
expr: /^–/gmi,
replacement: "&qdash;", //Tags have been substituted out, so we don't need to worry about duplication.
reason: App.consts.reasons.silent,
notAlone: true,
},
ndash_clear_left: { //For an ndash that we've added, make it so there's a space to the left.
expr: / *–/gmi,
replacement: " –",
reason: App.consts.reasons.silent,
notAlone: true,
},
ndash_unprotect_start: { //Reverse the protection
expr: /&qdash;/gmi,
replacement: "–", //Tags have been substituted out, so we don't need to worry about duplication.
reason: App.consts.reasons.silent,
notAlone: true,
},
ndash_clear_right: { //For an ndash that we've added, make it so there's a space, or whatever spaces already existed and a line-break.
expr: /–(?! +$) */gmi,
replacement: "– ",
reason: App.consts.reasons.silent,
notAlone: true
},
trailing_space: { // https://regex101.com/r/iQ0yR8/1
expr: /([^ ])[ ]{1}$/gm,
replacement: "$1",
debug: false,
reason: App.consts.reasons.silent
},
// The title says it all
thetitlesaysitall: {
// https://regex101.com/r/bX1qB4/3
expr: /(?:the )?title says (?:it all|everything)[.?!]*/gi,
replacement: function(){
return App.selections.title.val().replace(/[.?!]*$/,"? \n\n");
},
reason: App.consts.reasons.titleSaysAll
}
};
//Clear the global values which hold replacements
App.funcs.clearPlaceHolders = function() {
App.globals.placeHolderKeys.forEach(function(key) {
App.globals.replacedStrings[key] = [];
App.globals.replacedStringsOriginal[key] = [];
App.globals.placeHolderChecks[key] = new RegExp(App.globals.placeHolders[key],'gi');
});
};
App.funcs.clearPlaceHolders();
// Check if the placeholders are the same in two pieces of text.
App.funcs.didPlaceholdersChange = function(before, after) {
//Currently, we only check that the number of instances for each type of placeholder is the
// same in both texts.
return App.globals.placeHolderKeys.some(function(key) {
var regEx = App.globals.placeHolderChecks[key];
regEx.lastIndex = 0;
var beforeMatches = before.match(regEx);
regEx.lastIndex = 0;
var afterMatches = after.match(regEx);
return !((beforeMatches === null && afterMatches === null) || (beforeMatches !== null && afterMatches !== null && beforeMatches.length === afterMatches.length));
});
};
// This is where the magic happens: this function takes a few pieces of information and applies edits to the post
App.funcs.fixIt = function(input, edit, editRule) {
var expression = edit.expr;
var replacement = edit.replacement;
var reasoning = edit.reason;
var debug = edit.debug;
if (debug) {
console.log('editRule:', editRule);
console.log('input:', input);
console.log('expression.toString():', expression.toString());
console.log("replacement: '"+replacement+"'");
}
// If there is nothing to search, exit
if (!input) return false;
// Scan the post text using the expression to see if there are any matches
var originalInput = input;
var matches = input.match(expression);
if (debug) console.log('matches:', matches, ':: expression.exec(input)', expression.exec(input));
if (!matches) return false;
var count = 0; // # replacements to do
var deniedCount = 0; // # replacements not to do
input = input.replace(expression, function(before){
var after = before.replace(expression, replacement);
if(after !== before) ++count;
//Check to see if the quantity of the place holders changed between the input and output.
if(App.funcs.didPlaceholdersChange(before, after)) {
//An edit rule should never change the quantity of placeholders in the text. If it does, we prevent making the change.
//This will prevent individual changes where they affect an entire placeholder, but won't catch changes where the part
// of a placeholder is changed. To prevent that we have to also check all the changes vs. the original, complete input.
console.log('PREVENTED change: edit rule:', editRule, ': Placeholders changed: before:\n', before, ':: after:\n', after, '\n:: count:', count);
count--;
deniedCount++;
return before;
}
if (debug) console.log('before:', before, ':: after:', after, ':: after !== before:', after !== before, ':: count:', count);
return after;
});
if(App.funcs.didPlaceholdersChange(originalInput, input)) {
console.log('PREVENTED change group: edit rule:', editRule, ': Placeholders changed: originalInput:\n', originalInput, '\n:: input:\n', input, '\n:: count:', count);
input = originalInput;
}
if (!count && !deniedCount) {
// Seems like no replacements, check.
// In some cases, the expression matches on the initial input, but
// fails to on the individual matches. In that case, we can't count
// the total changes accurately, but we can still complete the
// replacement on the initial input.
var after = input.replace(expression, replacement);
if(App.funcs.didPlaceholdersChange(input, after)) {
//An edit rule should never change the quantity of placeholders in the text. If it does, we prevent making the change.
console.log('PREVENTED global change: edit rule:', editRule, ': Placeholders changed: input:\n', input, '\n:: after:\n', after, '\n:: count:', count);
after = input;
}
if (debug) console.log("zero-count: ", input, after, after !== input);
if(after !== input) {
++count;
input = after;
}
}
return count > 0 ? {
reason: reasoning,
fixed: String(input),
count: count
} : false;
};
// Populate or refresh DOM selections
App.funcs.popSelections = function() {
App.selections.redoButton = App.globals.root.find('[id^="wmd-redo-button"]');
App.selections.body = App.globals.root.find('[id^="wmd-input"]');
App.selections.title = App.globals.root.find('#title');
App.selections.summary = App.globals.root.find('[id^="edit-comment"], .edit-comment');
App.selections.tagField = App.globals.root.find(".tag-editor");
App.selections.submitButton = App.globals.root.find('[id^="submit-button"]');
App.selections.helpButton = App.globals.root.find('[id^="wmd-help-button"]');
App.selections.editor = App.globals.root.find('.post-editor');
App.selections.preview = App.globals.root.find('.wmd-preview');
App.selections.previewMenu = App.globals.root.find('.preview-options').append(' ');
if(!App.selections.previewMenu.length) {
App.selections.previewMenu = $('').insertBefore(App.selections.preview);
var previewToggleText = App.selections.preview.is(':visible') ? 'hide preview' : 'show preview';
App.selections.previewToggle = $('' + previewToggleText + '').click(App.funcs.togglePreview).appendTo(App.selections.previewMenu);
App.selections.previewMenu.append(' ');
} else {
App.selections.previewToggle = App.globals.root.find('.hide-preview').off('click').attr('href','javascript:void(0)').click(App.funcs.togglePreview);
}
App.selections.diffToggle = $('show diff').click(App.funcs.toggleDiff).appendTo(App.selections.previewMenu);
App.selections.diff = $('').hide().appendTo(App.selections.editor);
};
App.funcs.showPreview = function() {
App.selections.diff.hide();
App.selections.diffToggle.text('show diff');
App.selections.preview.show();
App.selections.previewToggle.text('hide preview');
};
App.funcs.showDiff = function() {
App.selections.preview.hide();
App.selections.previewToggle.text('show preview');
App.selections.diff.show();
App.selections.diffToggle.text('hide diff');
};
App.funcs.togglePreview = function() {
App.selections.diff.hide();
App.selections.diffToggle.text('show diff');
if(/hide/.test(App.selections.previewToggle.text())) return App.selections.previewToggle.text('show preview'), App.selections.preview.toggle(), false;
if(/show/.test(App.selections.previewToggle.text())) return App.selections.previewToggle.text('hide preview'), App.selections.preview.toggle(), false;
return false;
};
App.funcs.toggleDiff = function() {
App.selections.preview.hide();
App.selections.previewToggle.text('show preview');
if(/hide/.test(App.selections.diffToggle.text())) return App.selections.diffToggle.text('show diff'), App.selections.diff.toggle(), false;
if(/show/.test(App.selections.diffToggle.text())) return App.selections.diffToggle.text('hide diff'), App.selections.diff.toggle(), false;
};
// Populate edit item sets from DOM selections
App.funcs.popItems = function() {
var i = App.items, s = App.selections;
['title', 'body', 'summary'].forEach(function(v) {
i[v] = s[v].length ? s[v].val() : '';
});
};
// Populate original item sets from edit items for the diff
App.funcs.popOriginals = function() {
var i = App.originals, s = App.items;
['title', 'body', 'summary'].forEach(function(v) {
i[v] = s[v];
});
};
// Insert editing button
App.funcs.createButton = function() {
if (!App.selections.redoButton.length) return false;
App.selections.buttonWrapper = $('');
App.selections.buttonFix = $('' +
'' +
' ' +
'' +
'');
App.selections.buttonInfo = $('');
// Build the button
App.selections.buttonWrapper.append(App.selections.buttonFix);
App.selections.buttonWrapper.append(App.selections.buttonInfo);
// Insert button
App.selections.redoButton.after(App.selections.buttonWrapper);
// Attach the event listener to the button
App.selections.buttonFix.click(App.funcs.fixEvent);
App.selections.buttonWrapper.css({
'margin-left': '40px',
'display': 'inline-block',
'overflow': 'visible',
'white-space': 'nowrap'
});
App.selections.buttonFix.css({
'display': 'inline-block',
'background-image': 'none',
});
App.selections.buttonInfo.css({
'position': 'static',
'display': 'inline-block',
'vertical-align': 'bottom',
'margin-left': '5px',
'font-size': '12px',
'color': 'var(--white)',
'background': 'var(--black-800)',
'border-radius': '3px',
'padding': '3px 6px'
}).hide();
};
App.funcs.fixEvent = function() {
App.funcs.clearPlaceHolders();
return App.funcs.popItems(), App.pipe(App.items, App.pipeMods, App.globals.order), false;
};
App.funcs.diff = function(a1, a2) {
var strings = [];
function maakRij(type, rij) {
if (!type) return strings.push(rij.replace(/\' + rij.replace(/\'), true;
if (type === '-') return strings.push('' + rij.replace(/\'), true;
}
function getDiff(matrix, b1, b2, x, y) {
if (x > 0 && y > 0 && b1[y - 1] === b2[x - 1]) {
getDiff(matrix, b1, b2, x - 1, y - 1);
maakRij(false, b1[y - 1]);
} else {
if (x > 0 && (y === 0 || matrix[y][x - 1] >= matrix[y - 1][x])) {
getDiff(matrix, b1, b2, x - 1, y);
maakRij('+', b2[x - 1]);
} else if (y > 0 && (x === 0 || matrix[y][x - 1] < matrix[y - 1][x])) {
getDiff(matrix, b1, b2, x, y - 1);
maakRij('-', b1[y - 1]);
}
}
}
a1 = a1.split(/(?=\b|\W|_)/g);
a2 = a2.split(/(?=\b|\W|_)/g);
var matrix = new Array(a1.length + 1);
var x, y;
for (y = 0; y < matrix.length; y++) {
matrix[y] = new Array(a2.length + 1);
for (x = 0; x < matrix[y].length; x++) {
matrix[y][x] = 0;
}
}
for (y = 1; y < matrix.length; y++) {
for (x = 1; x < matrix[y].length; x++) {
if (a1[y - 1] === a2[x - 1]) {
matrix[y][x] = 1 + matrix[y - 1][x - 1];
} else {
matrix[y][x] = Math.max(matrix[y - 1][x], matrix[y][x - 1]);
}
}
}
try {
getDiff(matrix, a1, a2, x - 1, y - 1);
return strings.join('');
} catch (e) {
console.log(e);
}
};
// Pipe data through modules in proper order, returning the result
App.pipe = function(data, mods, order) {
var modName;
for (var i in order) {
if (order.hasOwnProperty(i)) {
modName = order[i];
mods[modName](data);
}
}
};
App.pipeMods.omit = function(data) {
if (!data.body) return false;
for (var type in App.globals.checks) {
if (App.globals.checks.hasOwnProperty(type)) {
data.body = data.body.replace(App.globals.checks[type], function(match) { // eslint-disable-line no-loop-func
App.globals.replacedStrings[type].push(match);
App.globals.replacedStringsOriginal[type].push(match);
return App.globals.placeHolders[type];
});
}
}
return data;
};
App.pipeMods.codefix = function() {
var replaced = App.globals.replacedStrings.block;
for (var i in replaced) {
// https://regex101.com/r/tX9pM3/1 https://regex101.com/r/tX9pM3/2 https://regex101.com/r/tX9pM3/3
if (/^`[^]+`$/.test(replaced[i])) replaced[i] = '\n\n' + /(?!`)((?!`)[^])+/.exec(replaced[i])[0].replace(/(.+)/g, ' $1');
}
};
App.pipeMods.inlineImages = function(data) {
//This only attempts to substitute image links in the format [foo][n]. It doesn't do [foo](URL.png)
if (!data.body) return false;
var links = App.globals.replacedStrings.links.filter(function(link) {
return /^\s*\[[^\[]*\]\s*\[\d+\]\s*$/.test(link);
});
var linkNumbers = links.map(function(link) {
return link.match(/^\s*\[[^\[]*\]\s*\[(\d+)\]\s*$/)[1];
});
//Find if matching https://i.stack.imgur.com/*.png link.
var imageNumbers = linkNumbers.filter(function(link, index) {
var testPng = new RegExp('^\\s*\\[' + linkNumbers[index] + '\\]:\\s*https?:\\/\\/i\\.stack\\.imgur\\.com\\/.*\\.(?:png|gif|jpg|jpeg|tif|tiff|bmp)\s*$', 'm');
return App.globals.replacedStrings.lsec.some(function(section) {
return testPng.test(section);
});
});
var replacements = 0;
imageNumbers.forEach(function(num) {
var replaceLink = new RegExp('^(\\s*)\\[([^\\[]*)\\](\\s*\\[' + num + '\\])(\\s*)$','');
App.globals.replacedStrings.links.forEach(function(link, index, array) {
array[index] = link.replace(replaceLink, '$2: \n$1[![$2]$3][' + num + ']$4').replace(/^enter image description here: {2}\n/,'');
if(array[index] !== link) {
replacements++;
}
});
});
if(replacements) {
if ('inlineImage' in App.globals.reasons) {
App.globals.reasons.inlineImage.count += replacements;
} else {
App.globals.reasons.inlineImage = { reason:'inline image' + (replacements > 1 ? 's' : ''), editId:'inlineImage', count:replacements };
}
}
return data;
};
App.pipeMods.edit = function(data) {
App.funcs.popOriginals();
var defaultBgColor = App.selections.body.css("background-color");
var flashColor = colour2rgb(retrieveCSSVariable("--green-200"));
// Visually confirm edit - SE makes it easy because the jQuery color animation plugin seems to be there by default
App.selections.body.animate({ backgroundColor: flashColor }, 10);
App.selections.body.animate({ backgroundColor: defaultBgColor }, 1000);
// List of fields to be edited
var fields = {body:'body',title:'title'};
function applyEditRules(ruleKeyList, notAlone) {
ruleKeyList = typeof ruleKeyList === 'string' ? [ruleKeyList] : ruleKeyList;
if (!Array.isArray(ruleKeyList)) {
return false;
}
var changes = false;
ruleKeyList.forEach(function(ruleKey) {
changes = applyEditRule(ruleKey, notAlone) || changes;
});
return changes;
}
function applyEditRule(ruleKey, notAlone) {
const editRule = App.edits[ruleKey];
const debug = editRule.debug;
var rerunChanges = false;
var changes = false;
if (editRule.notAlone && !notAlone) {
if (debug) console.log("edit " + ruleKey + " skipped: not alone");
return false;
}
if (debug && editRule.runBefore) console.log("edit " + ruleKey + ": running rules before:", editRule.runBefore);
var beforeChanges = applyEditRules(editRule.runBefore, true);
for (var field in fields) {
if (fields.hasOwnProperty(field)) {
if (debug) console.log("edit " + ruleKey + " in " + field);
if ((editRule.titleOnly && 'title' !== field) || (editRule.bodyOnly && 'body' !== field)) {
continue; // Skip title-only edits if not editing title, or the same for bodies.
}
var fix = App.funcs.fixIt(data[field], editRule, ruleKey);
if (!fix) continue;
changes = true;
//A change was made:
console.log('Change by edit rule: reason:', editRule.reason, ':: ruleKey:', ruleKey, ':: editRule', editRule, ':: before:', {before: data[field]}, ':: fix:', fix);
if (fix.reason in App.globals.reasons) {
App.globals.reasons[fix.reason].count += fix.count;
} else {
App.globals.reasons[fix.reason] = { reason:fix.reason, editId:ruleKey, count:fix.count };
}
data[field] = fix.fixed;
editRule.fixed = true;
}
}
if (changes && editRule.rerun) {
if (debug) console.log("edit " + ruleKey + ": re-running rules:", editRule.rerun);
rerunChanges = applyEditRules(editRule.rerun, true);
}
if (debug && editRule.runAfter) console.log("edit " + ruleKey + ": running rules After:", editRule.runAfter);
var afterChanges = applyEditRules(editRule.runAfter, true);
return beforeChanges || changes || rerunChanges || afterChanges;
}
// Loop through all editing rules
applyEditRules(Object.keys(App.edits));
// Remove silent change reason
delete App.globals.reasons[App.consts.reasons.silent];
// If there are no reasons, exit
if (App.globals.reasons == {}) return false;
// We need a place to store the reasons being applied to the summary.
var reasons = [];
App.globals.changes = 0;
for (var z in App.globals.reasons) {
if (App.globals.reasons.hasOwnProperty(z)) {
// For each type of change made, add a reason string with the reason text,
// optionally the rule ID, and the number of repeats if 2 or more.
reasons.push(App.globals.reasons[z].reason
+ (App.globals.showRules ? ' ['+ App.globals.reasons[z].editId +']' : '')
+ (App.globals.showCounts ? ((App.globals.reasons[z].count > 1) ? ' ('+App.globals.reasons[z].count+')' : '') : '') );
App.globals.changes += App.globals.reasons[z].count;
}
}
var reasonStr = reasons.length ? reasons.join('; ')+'.' : ''; // Unique reasons separated by ; and terminated by .
if (!data.hasOwnProperty('summaryOrig')) {
// Remember original summary
data.summaryOrig = data.summary.trim().replace(/([^;])[.?!:]?$/,"$1;");
}
if (data.summaryOrig.length) {
data.summaryOrig += ' ';
} else {
reasonStr = reasonStr.charAt(0).toUpperCase() + reasonStr.slice(1); // Cap first letter.
}
data.summary = data.summaryOrig + reasonStr;
// Limit summary to 300 chars
if (data.summary.length > 300) {
data.summary = data.summary.substr(0,300-3) + '...';
}
return data;
};
// Populate the diff
App.pipeMods.diff = function() {
App.selections.diff.empty().append('' + App.funcs.diff(App.originals.title, App.items.title, true) + '' +
'' + App.pipeMods.replace({body:App.funcs.diff(App.originals.body, App.items.body)}, true).body + '');
App.funcs.showDiff();
};
// Replace the previously omitted code
App.pipeMods.replace = function(data, literal) {
if (!data.body) return false;
for (var type in App.globals.checksr) {
if (App.globals.checksr.hasOwnProperty(type)) {
var i = 0;
data.body = data.body.replace(App.globals.placeHolderChecks[type], function() { // eslint-disable-line no-loop-func
var replace = App.globals.replacedStrings[type][i++];
if(literal && /block|lsec/.test(type)) {
var after = replace.replace(/^\n\n/,'');
var prepend = after !== replace ? '\n\n`' : '';
var append = after !== replace ? '`' : '';
var klass = /lsec/.test(type) ? ' class="lang-none prettyprint prettyprinted"' : '';
return prepend + '' + after.replace(/
' + append;
}
if(literal && /quote/.test(type)) return '' + replace.replace(//gm,'') + '
';
if(literal) return '' + replace.replace(/';
return replace;
});
}
}
return data;
};
// Handle pipe output
App.pipeMods.output = function(data) {
App.selections.title.val(data.title);
App.selections.body.val(data.body.replace(/\n{3,}/,'\n\n'));
App.selections.summary.val(data.summary);
App.globals.root.find('.actual-edit-overlay').remove();
App.selections.summary.css({opacity:1});
App.selections.buttonInfo.text(App.globals.changes).show();
StackExchange.MarkdownEditor.refreshAllPreviews();
};
// Init app
App.init = function() {
var count = 0;
var toolbarchk = setInterval(function(){
if(++count === 10) clearInterval(toolbarchk);
if(!App.globals.root.find('.wmd-button-row').length) return;
clearInterval(toolbarchk);
App.funcs.popSelections();
App.funcs.createButton();
}, 100);
return App;
};
return App.init();
}
try {
StackExchange.using('inlineEditing', function() {
StackExchange.ready(function() {
var test = window.location.href.match(/.posts.(\d+).edit/);
if(test) {
extendEditor($('form[action^="/posts/' + test[1] + '"]'));
}
$('#post-form').each(function(){
extendEditor($(this));
});
});
});
$(document).ajaxComplete(function() {
var test = arguments[2].url.match(/posts.(\d+).edit-inline/);
if(!test) {
test = arguments[2].url.match(/review.inline-edit-post/);
if(!test) return;
test = arguments[2].data.match(/id=(\d+)/);
if(!test) return;
}
StackExchange.ready(function() {
extendEditor($('form[action^="/posts/' + test[1] + '"]'));
});
});
// This is the styling for the diff output.
$('body').append('');
} catch (e) {
console.log(e);
}
/* eslint-disable */
/*
* To Title Case 2.1 – http://individed.com/code/to-title-case/
* Copyright © 2008–2013 David Gouch. Licensed under the MIT License.
* It has been modified to be a function call, rather than added to the String prototype.
*/
//This is function call, rather than a method on the String prototype, because a userscript, unless it's intended
// purpose is to make such a basic change, shouldn't be making a change to the prototype of a built-in type.
// Changing the prototype of a built-in has a significant chance of causing compatibility issues.
function toTitleCase(text){
var smallWords = /^(a|an|and|as|at|but|by|en|for|if|in|nor|of|on|or|per|the|to|vs?\.?|via)$/i;
return text.replace(/[A-Za-z0-9\u00C0-\u00FF]+[^\s-]*/g, function(match, index, title){
if (index > 0 && index + match.length !== title.length &&
match.search(smallWords) > -1 && title.charAt(index - 2) !== ":" &&
(title.charAt(index + match.length) !== '-' || title.charAt(index - 1) === '-') &&
title.charAt(index - 1).search(/[^\s-]/) < 0) {
return match.toLowerCase();
}
if (match.substr(1).search(/[A-Z]|\../) > -1) {
return match;
}
return match.charAt(0).toUpperCase() + match.substr(1);
});
};
// From https://github.com/EamonNerbonne/a-vs-an
var AvsAnSimple=function(n){function i(n){var r=parseInt(t,36)||0,f=r&&r.toString(36).length,u,e;for(n.article=t[f]=="."?"a":"an",t=t.substr(1+f),u=0;u=0);for(;;){if(u=i.article||u,i=i[r],!i)return u;r=t[f++]||" "}}}}({})
// Adapted from http://stackoverflow.com/a/6969486/1677912
function escapeTag(tag) {
// See https://regex101.com/r/yW9cD4/1
var retag = tag.replace(/(?:(\-)|([+.#]))/g,
function (match, hyphen, other) {
var escaped = (hyphen) ? "[ \\-]" : "\\"+match;
return escaped;
});
return "(?:\\s|\\b|$)" + retag + "(?:\\s|\\b|$)"; // hack - enclose tag in regexp boundary checks. WBN to do this in the taglist regexp.
}
/**
* Pass a CSS variable to get its value
* @param {string} val - for example "--black" or "--green-600"
*/
function retrieveCSSVariable(val) {
return getComputedStyle(document.body)
.getPropertyValue(val);
}
/**
* Converts an arbitrary colour representation to an RGB string representation. Invalid colours might return "rgb(0,0,0)". The conversioon is done by
* offloading the interpreting the string to a canvas - common strings like hex or HSL would be supported but perhapos not all named colours would be.
*
* Based on the code by Aaron Watters: https://stackoverflow.com/a/52044517
*
* @param {string} string - any colour representation, for example: "salmon", "#FA8072", "#fa8072", hsl(6,93%,71%), hsl(6, 93%, 71%)
* @returns {string} - String of the format: "rgb(250,126,113)". Invalid input would produce black "rgb(0,0,0)"
*/
function colour2rgb(string) {
var canvas = document.createElement("canvas");
//make 1x1 px rectangle in the arbitrary colour
var context = canvas.getContext("2d");
context.beginPath();
context.rect(0,0,1,1);
context.fillStyle = string;
context.fill();
//extract the three primary colours and omit the alpha channel information
var rgbData = context.getImageData(0, 0, 1, 1).data.slice(0, 3);
return "rgb(" + rgbData.join(",") + ")";
}
// Better handling of indentation and the TAB key when editing posts
// From balpha's stackexchange-tab-editing
// (c) 2012 Benjamin Dumke-von der Ehe
// Which is released under the MIT License - https://opensource.org/licenses/MIT
// See http://stackapps.com/questions/3247/better-handling-of-indentation-and-the-tab-key-when-editing-posts
// Current version: 2.0.0, from https://github.com/mogsdad/UserScripts/blob/master/tab-editing.user.js
function with_jquery(t){var e=document.createElement("script");e.type="text/javascript",e.textContent="("+t.toString()+")(jQuery)",document.body.appendChild(e)}with_jquery(function(t){t(function(){if(window.StackExchange&&StackExchange.ready){var e=4,n=" ".repeat(e),r="selectionDirection"in t("")[0],i={9:{handler:u,allowShift:!0},36:{handler:function(n){var r,i,a=t(this),c=this.value||"",h=s(a),u=c.substring(0,h.directedEnd),l=c.substring(h.directedEnd),o=(u.match(/(?:^|\n)([^\n]*)$/)||["",""])[1];if(o.length){r=o.search(/[^ \t]/);var f=new RegExp("^ {0,"+(e-1)+"}\t");(r>=e||r>0&&f.test(o))&&(i=h.directedEnd-o.length+r)}else(r=(l.match(/^[\t ]*/)||[""])[0].length)>0&&(i=h.directedEnd+r);if(void 0!==i)return n?d(a,h.directedStart,i):d(a,i),!1;return!0},allowShift:r},8:{handler:function(){var e=t(this),n=this.value||"",r=s(e),i=(n.substring(0,r.end).match(/(?:^|\n)([^\n]*)$/)||["",""])[1];if(r.start===r.end&&/^[ \t]+$/.test(i))return u.call(this,!0);return!0}}},a=!0,c=!1;t("#mainbar").on("keyup",".wmd-input",function(t){c&&17===t.which?h(this):h(this,!0)}),t("#mainbar").on("keydown",".wmd-input",function(t){if(c=17===t.which,t.ctrlKey||t.altKey||t.metaKey)return!0;var e=a;if(h(this,!0),!e)return!0;if(!i.hasOwnProperty(t.which))return!0;var n=i[t.which];return!(!t.shiftKey||n.allowShift)||n.handler.call(this,t.shiftKey)})}function s(t){var e=t.caret(),n="backward"===t[0].selectionDirection;return e.end-=e.text.match(/([ \t\n]*)$/)[0].length,n?(e.directedStart=e.end,e.directedEnd=e.start,e.backward=!0):(e.directedStart=e.start,e.directedEnd=e.end),e}function d(t,e,n,i){var a,c;2===arguments.length&&(n=e),i&&(a=e,e=n,n=a),e>n&&(a=e,e=n,n=a,c=!0),c&&r?t[0].setSelectionRange(e,n,"backward"):t.caret(e,n)}function h(e,n){void 0===n&&(n=!a),a^n&&(t(e).css("opacity",n?1:.3),a=n)}function u(r){var i,a,c=t(this),h=this.value||"",u=s(c),l=h.substring(0,u.start),o=h.substring(u.start,u.end),f=h.substring(u.end);if(u.start===u.end){var g,v=(l.match(/(?:^|\n)([^\n]*)$/)||["",""])[1],w=0;if(r&&!/(^|[ \t])$/.test(v))return!1;var p=!0;for(r&&!v.length&&(f=f.replace(/^[ \t]*/,function(t){return v=t,l+=t,""}),p=!1),g=0;g