Diff
checker
文本
文本
图像
文档
Excel
文件夹
Legal
Enterprise
桌面版
定价
登录
下载 Diffchecker 桌面版
比较文本
查找两个文本文件之间的差异
工具
历史
实时编辑器
折叠未更改行
关闭换行
视图
拆分
统一
比对精度
智能
单词
字符
语法高亮
选择语法
忽略
文本转换
转到第一个差异
编辑输入
Diffchecker Desktop
运行Diffchecker最安全的方式。获取Diffchecker桌面应用:您的差异永远不会离开您的电脑!
获取桌面版
Untitled diff
创建于
11年前
差异永不过期
清除
导出
分享
解释
26 删除
行
总计
删除
字符
总计
删除
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
585 行
全部复制
35 添加
行
总计
添加
字符
总计
添加
要继续使用此功能,请升级到
Diff
checker
Pro
查看价格
590 行
全部复制
// ==UserScript==
// ==UserScript==
// @name Stack-Exchange-Editor-Toolkit
// @name Stack-Exchange-Editor-Toolkit
// @author Cameron Bernhardt (AstroCB)
// @author Cameron Bernhardt (AstroCB)
// @developer Jonathan Todd (jt0dd)
// @developer Jonathan Todd (jt0dd)
// @developer sathyabhat
// @developer sathyabhat
// @contributor Unihedron
// @contributor Unihedron
// @contributor Tiny Giant
// @contributor Tiny Giant
// @contributor Mogsdad
// @contributor Mogsdad
// @grant none
// @grant none
// @license MIT
// @license MIT
// @namespace http://github.com/AstroCB
// @namespace http://github.com/AstroCB
复制
已复制
复制
已复制
// @version 1.5.2.
4
// @version 1.5.2.
5
// @description Fix common grammar/usage annoyances on Stack Exchange posts with a click
// @description Fix common grammar/usage annoyances on Stack Exchange posts with a click
// @include *://*.stackexchange.com/questions/*
// @include *://*.stackexchange.com/questions/*
// @include *://stackoverflow.com/questions/*
// @include *://stackoverflow.com/questions/*
// @include *://stackoverflow.com/review/helper/*
// @include *://stackoverflow.com/review/helper/*
// @include *://meta.stackoverflow.com/questions/*
// @include *://meta.stackoverflow.com/questions/*
// @include *://serverfault.com/questions/*
// @include *://serverfault.com/questions/*
// @include *://meta.serverfault.com/questions/*
// @include *://meta.serverfault.com/questions/*
// @include *://superuser.com/questions/*
// @include *://superuser.com/questions/*
// @include *://meta.superuser.com/questions/*
// @include *://meta.superuser.com/questions/*
// @include *://askubuntu.com/questions/*
// @include *://askubuntu.com/questions/*
// @include *://meta.askubuntu.com/questions/*
// @include *://meta.askubuntu.com/questions/*
// @include *://stackapps.com/questions/*
// @include *://stackapps.com/questions/*
// @include *://*.stackexchange.com/posts/*
// @include *://*.stackexchange.com/posts/*
// @include *://stackoverflow.com/posts/*
// @include *://stackoverflow.com/posts/*
// @include *://meta.stackoverflow.com/posts/*
// @include *://meta.stackoverflow.com/posts/*
// @include *://serverfault.com/posts/*
// @include *://serverfault.com/posts/*
// @include *://meta.serverfault.com/posts/*
// @include *://meta.serverfault.com/posts/*
// @include *://superuser.com/posts/*
// @include *://superuser.com/posts/*
// @include *://meta.superuser.com/posts/*
// @include *://meta.superuser.com/posts/*
// @include *://askubuntu.com/posts/*
// @include *://askubuntu.com/posts/*
// @include *://meta.askubuntu.com/posts/*
// @include *://meta.askubuntu.com/posts/*
// @include *://stackapps.com/posts/*
// @include *://stackapps.com/posts/*
// @include *://*.stackexchange.com/review/*
// @include *://*.stackexchange.com/review/*
// @include *://stackoverflow.com/review/*
// @include *://stackoverflow.com/review/*
// @include *://meta.stackoverflow.com/review/*
// @include *://meta.stackoverflow.com/review/*
// @include *://serverfault.com/review/*
// @include *://serverfault.com/review/*
// @include *://meta.serverfault.com/review/*
// @include *://meta.serverfault.com/review/*
// @include *://superuser.com/review/*
// @include *://superuser.com/review/*
// @include *://meta.superuser.com/review/*
// @include *://meta.superuser.com/review/*
// @include *://askubuntu.com/review/*
// @include *://askubuntu.com/review/*
// @include *://meta.askubuntu.com/review/*
// @include *://meta.askubuntu.com/review/*
// @include *://stackapps.com/review/*
// @include *://stackapps.com/review/*
// @exclude *://*.stackexchange.com/questions/tagged/*
// @exclude *://*.stackexchange.com/questions/tagged/*
// @exclude *://stackoverflow.com/questions/tagged/*
// @exclude *://stackoverflow.com/questions/tagged/*
// @exclude *://meta.stackoverflow.com/questions/tagged/*
// @exclude *://meta.stackoverflow.com/questions/tagged/*
// @exclude *://serverfault.com/questions/tagged/*
// @exclude *://serverfault.com/questions/tagged/*
// @exclude *://meta.serverfault.com/questions/*
// @exclude *://meta.serverfault.com/questions/*
// @exclude *://superuser.com/questions/tagged/*
// @exclude *://superuser.com/questions/tagged/*
// @exclude *://meta.superuser.com/questions/tagged/*
// @exclude *://meta.superuser.com/questions/tagged/*
// @exclude *://askubuntu.com/questions/tagged/*
// @exclude *://askubuntu.com/questions/tagged/*
// @exclude *://meta.askubuntu.com/questions/tagged/*
// @exclude *://meta.askubuntu.com/questions/tagged/*
// @exclude *://stackapps.com/questions/tagged/*
// @exclude *://stackapps.com/questions/tagged/*
// ==/UserScript==
// ==/UserScript==
var main = function() {
var main = function() {
// Define app namespace
// Define app namespace
复制
已复制
复制
已复制
function EditorToolkit(
targetID) {
function EditorToolkit(
inline,
targetID) {
if (!(this instanceof EditorToolkit)) return false;
if (!(this instanceof EditorToolkit)) return false;
var App = this;
var App = this;
// Place edit items here
// Place edit items here
App.items = [];
App.items = [];
// Place selected jQuery items here
// Place selected jQuery items here
App.selections = {};
App.selections = {};
// Place "global" app data here
// Place "global" app data here
App.globals = {};
App.globals = {};
// Place "helper" functions here
// Place "helper" functions here
App.funcs = {};
App.funcs = {};
//Preload icon alt
//Preload icon alt
var SEETicon = new Image();
var SEETicon = new Image();
SEETicon.src = '//i.imgur.com/d5ZL09o.png';
SEETicon.src = '//i.imgur.com/d5ZL09o.png';
// Define variables for later use
// Define variables for later use
App.globals.barReady = false;
App.globals.barReady = false;
App.globals.editsMade = false;
App.globals.editsMade = false;
App.globals.editCount = 0;
App.globals.editCount = 0;
App.globals.infoContent = '';
App.globals.infoContent = '';
// Check if there was an ID passed (if not, use question ID from URL);
// Check if there was an ID passed (if not, use question ID from URL);
if (!targetID) targetID = window.location.href.match(/\/(\d+)\//g)[0].split("/").join("");
if (!targetID) targetID = window.location.href.match(/\/(\d+)\//g)[0].split("/").join("");
App.globals.targetID = targetID;
App.globals.targetID = targetID;
复制
已复制
复制
已复制
App.globals.inline = inline;
App.globals.spacerHTML = '<li class="wmd-spacer wmd-spacer3" id="wmd-spacer3-' + App.globals.targetID + '" style="left: 400px !important;"></li>';
App.globals.spacerHTML = '<li class="wmd-spacer wmd-spacer3" id="wmd-spacer3-' + App.globals.targetID + '" style="left: 400px !important;"></li>';
App.selections.buttonWrapper = $('<div class="ToolkitButtonWrapper"/>');
App.selections.buttonWrapper = $('<div class="ToolkitButtonWrapper"/>');
App.selections.buttonFix = $('<button class="wmd-button ToolkitFix" />');
App.selections.buttonFix = $('<button class="wmd-button ToolkitFix" />');
App.selections.buttonInfo = $('<div class="ToolkitInfo">');
App.selections.buttonInfo = $('<div class="ToolkitInfo">');
App.selections.buttonWrapper.append(App.selections.buttonFix);
App.selections.buttonWrapper.append(App.selections.buttonFix);
App.selections.buttonWrapper.append(App.selections.buttonInfo);
App.selections.buttonWrapper.append(App.selections.buttonInfo);
App.globals.reasons = [];
App.globals.reasons = [];
App.globals.numReasons = 0;
App.globals.numReasons = 0;
App.globals.replacedStrings = {
App.globals.replacedStrings = {
"block": [],
"block": [],
"inline": []
"inline": []
};
};
App.globals.placeHolders = {
App.globals.placeHolders = {
"block": "_xCodexBlockxPlacexHolderx_",
"block": "_xCodexBlockxPlacexHolderx_",
"inline": "_xCodexInlinexPlacexHolderx_"
"inline": "_xCodexInlinexPlacexHolderx_"
};
};
App.globals.checks = {
App.globals.checks = {
"block": /( )+.*/gm,
"block": /( )+.*/gm,
"inline": /`.*`/gm
"inline": /`.*`/gm
};
};
// Assign modules here
// Assign modules here
App.globals.pipeMods = {};
App.globals.pipeMods = {};
// Define order in which mods affect here
// Define order in which mods affect here
App.globals.order = ["omit", "edit", "replace"];
App.globals.order = ["omit", "edit", "replace"];
// Define edit rules
// Define edit rules
App.edits = {
App.edits = {
i: {
i: {
expr: /(^|\s|\()i(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
expr: /(^|\s|\()i(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
replacement: "$1I$2",
replacement: "$1I$2",
reason: "in English, the personal pronoun is 'I'"
reason: "in English, the personal pronoun is 'I'"
},
},
so: {
so: {
expr: /(^|\s)[Ss]tack\s*overflow|StackOverflow(.|$)/gm,
expr: /(^|\s)[Ss]tack\s*overflow|StackOverflow(.|$)/gm,
replacement: "$1Stack Overflow$2",
replacement: "$1Stack Overflow$2",
reason: "'Stack Overflow' is the legal name"
reason: "'Stack Overflow' is the legal name"
},
},
se: {
se: {
expr: /(^|\s)[Ss]tack\s*exchange|StackExchange(.|$)/gm,
expr: /(^|\s)[Ss]tack\s*exchange|StackExchange(.|$)/gm,
replacement: "$1Stack Exchange$2",
replacement: "$1Stack Exchange$2",
reason: "'Stack Exchange' is the legal name"
reason: "'Stack Exchange' is the legal name"
},
},
expansionSO: {
expansionSO: {
expr: /(^|\s)SO(\s|,|\.|!|\?|;|\/|\)|$)/gm,
expr: /(^|\s)SO(\s|,|\.|!|\?|;|\/|\)|$)/gm,
replacement: "$1Stack Overflow$2",
replacement: "$1Stack Overflow$2",
reason: "'SO' expansion"
reason: "'SO' expansion"
},
},
expansionSE: {
expansionSE: {
expr: /(^|\s)SE(\s|,|\.|!|\?|;|\/|\)|$)/gm,
expr: /(^|\s)SE(\s|,|\.|!|\?|;|\/|\)|$)/gm,
replacement: "$1Stack Exchange$2",
replacement: "$1Stack Exchange$2",
reason: "'SE' expansion"
reason: "'SE' expansion"
},
},
javascript: {
javascript: {
expr: /(^|\s)[Jj]ava\s*[Ss]cript(.|$)/gm,
expr: /(^|\s)[Jj]ava\s*[Ss]cript(.|$)/gm,
replacement: "$1JavaScript$2",
replacement: "$1JavaScript$2",
reason: "'JavaScript' is the proper capitalization"
reason: "'JavaScript' is the proper capitalization"
},
},
jsfiddle: {
jsfiddle: {
expr: /(^|\s)[Jj][Ss]\s*[Ff]iddle(.|$)/gm,
expr: /(^|\s)[Jj][Ss]\s*[Ff]iddle(.|$)/gm,
replacement: "$1JSFiddle$2",
replacement: "$1JSFiddle$2",
reason: "'JSFiddle' is the currently accepted capitalization"
reason: "'JSFiddle' is the currently accepted capitalization"
},
},
caps: {
caps: {
expr: /^(?!https?)([a-z])/gm,
expr: /^(?!https?)([a-z])/gm,
replacement: "$1",
replacement: "$1",
reason: "copy edited"
reason: "copy edited"
},
},
jquery: {
jquery: {
expr: /(^|\s)[Jj][Qq]uery(.|$)/gm,
expr: /(^|\s)[Jj][Qq]uery(.|$)/gm,
replacement: "$1jQuery$2",
replacement: "$1jQuery$2",
reason: "'jQuery' is the proper capitalization"
reason: "'jQuery' is the proper capitalization"
},
},
html: {
html: {
expr: /(^|\s)[Hh]tml([5]?)\b(\S|)(?!\S)/gm,
expr: /(^|\s)[Hh]tml([5]?)\b(\S|)(?!\S)/gm,
replacement: "$1HTML$2$3",
replacement: "$1HTML$2$3",
reason: "HTML stands for HyperText Markup Language"
reason: "HTML stands for HyperText Markup Language"
},
},
css: {
css: {
expr: /(^|\s)[Cc]ss\b(\S|)(?!\S)/gm,
expr: /(^|\s)[Cc]ss\b(\S|)(?!\S)/gm,
replacement: "$1CSS$2",
replacement: "$1CSS$2",
reason: "CSS stands for Cascading Style Sheets"
reason: "CSS stands for Cascading Style Sheets"
},
},
json: {
json: {
expr: /(^|\s)[Jj]son\b(\S|)(?!\S)/gm,
expr: /(^|\s)[Jj]son\b(\S|)(?!\S)/gm,
replacement: "$1JSON$2",
replacement: "$1JSON$2",
reason: "JSON stands for JavaScript Object Notation"
reason: "JSON stands for JavaScript Object Notation"
},
},
ajax: {
ajax: {
expr: /(^|\s)ajax\b(\S|)(?!\S)/gm,
expr: /(^|\s)ajax\b(\S|)(?!\S)/gm,
replacement: "$1AJAX$2",
replacement: "$1AJAX$2",
reason: "AJAX stands for Asynchronous JavaScript and XML"
reason: "AJAX stands for Asynchronous JavaScript and XML"
},
},
angular: {
angular: {
expr: /[Aa]ngular[Jj][Ss]/g,
expr: /[Aa]ngular[Jj][Ss]/g,
replacement: "AngularJS",
replacement: "AngularJS",
reason: "'AngularJS is the proper capitalization"
reason: "'AngularJS is the proper capitalization"
},
},
thanks: {
thanks: {
expr: /(thanks|pl(?:ease|z|s)\s+h[ea]lp|cheers|regards|thx|thank\s+you|my\s+first\s+question|kindly\shelp).*$/gmi,
expr: /(thanks|pl(?:ease|z|s)\s+h[ea]lp|cheers|regards|thx|thank\s+you|my\s+first\s+question|kindly\shelp).*$/gmi,
replacement: "",
replacement: "",
reason: "'$1' is unnecessary noise"
reason: "'$1' is unnecessary noise"
},
},
commas: {
commas: {
expr: /,([^\s])/g,
expr: /,([^\s])/g,
replacement: ", $1",
replacement: ", $1",
reason: "punctuation & spacing"
reason: "punctuation & spacing"
},
},
php: {
php: {
expr: /(^|\s)[Pp]hp\b(\S|)(?!\S)/gm,
expr: /(^|\s)[Pp]hp\b(\S|)(?!\S)/gm,
replacement: "$1PHP$2",
replacement: "$1PHP$2",
reason: "PHP stands for PHP: Hypertext Preprocessor"
reason: "PHP stands for PHP: Hypertext Preprocessor"
},
},
hello: {
hello: {
expr: /(?:^|\s)(hi\s+guys|hi|hello|good\s(?:evening|morning|day|afternoon))(?:\.|!|\ )/gmi,
expr: /(?:^|\s)(hi\s+guys|hi|hello|good\s(?:evening|morning|day|afternoon))(?:\.|!|\ )/gmi,
replacement: "",
replacement: "",
reason: "greetings like '$1' are unnecessary noise"
reason: "greetings like '$1' are unnecessary noise"
},
},
edit: {
edit: {
expr: /(?:^\**)(edit|update):?(?:\**):?/gmi,
expr: /(?:^\**)(edit|update):?(?:\**):?/gmi,
replacement: "",
replacement: "",
reason: "Stack Exchange has an advanced revision history system: 'Edit' or 'Update' is unnecessary"
reason: "Stack Exchange has an advanced revision history system: 'Edit' or 'Update' is unnecessary"
},
},
voting: {
voting: {
expr: /([Dd]own|[Uu]p)[\s*\-]vot/g,
expr: /([Dd]own|[Uu]p)[\s*\-]vot/g,
replacement: "$1vote",
replacement: "$1vote",
reason: "the proper spelling (despite the tag name) is '$1vote' (one word)"
reason: "the proper spelling (despite the tag name) is '$1vote' (one word)"
},
},
mysite: {
mysite: {
expr: /mysite\./g,
expr: /mysite\./g,
replacement: "example.",
replacement: "example.",
reason: "links to mysite.domain are not allowed: use example.domain instead"
reason: "links to mysite.domain are not allowed: use example.domain instead"
},
},
c: {
c: {
expr: /(^|\s)c(#|\++|\s|$)/gm,
expr: /(^|\s)c(#|\++|\s|$)/gm,
replacement: "$1C$2",
replacement: "$1C$2",
reason: "C$2 is the proper capitalization"
reason: "C$2 is the proper capitalization"
},
},
java: {
java: {
expr: /(^|\s)java\b(\S|)(?!\S)/gmi,
expr: /(^|\s)java\b(\S|)(?!\S)/gmi,
replacement: "$1Java$2",
replacement: "$1Java$2",
reason: "Java should be capitalized"
reason: "Java should be capitalized"
},
},
sql: {
sql: {
expr: /(^|\s)[Ss]ql\b(\S|)(?!\S)/gm,
expr: /(^|\s)[Ss]ql\b(\S|)(?!\S)/gm,
replacement: "$1SQL$2",
replacement: "$1SQL$2",
reason: "SQL is the proper capitalization"
reason: "SQL is the proper capitalization"
},
},
sqlite: {
sqlite: {
expr: /(^|\s)[Ss]qlite([0-9]*)\b(\S|)(?!\S)/gm,
expr: /(^|\s)[Ss]qlite([0-9]*)\b(\S|)(?!\S)/gm,
replacement: "$1SQLite$2$3",
replacement: "$1SQLite$2$3",
reason: "SQLite is the proper capitalization"
reason: "SQLite is the proper capitalization"
},
},
android: {
android: {
expr: /(^|\s)android\b(\S|)(?!\S)/gmi,
expr: /(^|\s)android\b(\S|)(?!\S)/gmi,
replacement: "$1Android$2",
replacement: "$1Android$2",
reason: "Android should be capitalized"
reason: "Android should be capitalized"
},
},
oracle: {
oracle: {
expr: /(^|\s)oracle\b(\S|)(?!\S)/gmi,
expr: /(^|\s)oracle\b(\S|)(?!\S)/gmi,
replacement: "$1Oracle$2",
replacement: "$1Oracle$2",
reason: "Oracle should be capitalized"
reason: "Oracle should be capitalized"
},
},
windows: {
windows: {
expr: /(win|windows(?:\ ?)(\s[0-9]+))\b(\S|)(?!\S)/igm,
expr: /(win|windows(?:\ ?)(\s[0-9]+))\b(\S|)(?!\S)/igm,
replacement: "Windows$2$3",
replacement: "Windows$2$3",
reason: "Windows should be capitalized"
reason: "Windows should be capitalized"
},
},
windowsXP: {
windowsXP: {
expr: /(win|windows(?:\ ?)(\sxp))\b(\S|)(?!\S)/igm,
expr: /(win|windows(?:\ ?)(\sxp))\b(\S|)(?!\S)/igm,
replacement: "Windows XP$3",
replacement: "Windows XP$3",
reason: "Windows XP should be capitalized"
reason: "Windows XP should be capitalized"
},
},
windowsVista: {
windowsVista: {
expr: /(win|windows(?:\ ?)(\svista))\b(\S|)(?!\S)/igm,
expr: /(win|windows(?:\ ?)(\svista))\b(\S|)(?!\S)/igm,
replacement: "Windows Vista$3",
replacement: "Windows Vista$3",
reason: "Windows Vista should be capitalized"
reason: "Windows Vista should be capitalized"
},
},
ubuntu: {
ubuntu: {
expr: /(ubunto|ubunut|ubunutu|ubunu|ubntu|ubutnu|ubanto[o]+|unbuntu|ubunt|ubutu)\b(\S|)(?!\S)/igm,
expr: /(ubunto|ubunut|ubunutu|ubunu|ubntu|ubutnu|ubanto[o]+|unbuntu|ubunt|ubutu)\b(\S|)(?!\S)/igm,
replacement: "Ubuntu$2",
replacement: "Ubuntu$2",
reason: "corrected Ubuntu spelling"
reason: "corrected Ubuntu spelling"
},
},
linux: {
linux: {
expr: /(linux)\b(\S|)(?!\S)/igm,
expr: /(linux)\b(\S|)(?!\S)/igm,
replacement: "Linux$2",
replacement: "Linux$2",
reason: "Linux should be capitalized"
reason: "Linux should be capitalized"
},
},
apostrophes: {
apostrophes: {
expr: /(^|\s)(can|doesn|don|won|hasn|isn|didn)t(\s|$)/gmi,
expr: /(^|\s)(can|doesn|don|won|hasn|isn|didn)t(\s|$)/gmi,
replacement: "$1$2't$3",
replacement: "$1$2't$3",
reason: "English contractions use apostrophes"
reason: "English contractions use apostrophes"
},
},
ios: {
ios: {
expr: /\b(?:ios|iOs|ioS|IOS|Ios|IoS|ioS)\b(\S|)(?!\S)/gm,
expr: /\b(?:ios|iOs|ioS|IOS|Ios|IoS|ioS)\b(\S|)(?!\S)/gm,
replacement: "iOS$1",
replacement: "iOS$1",
reason: "the proper usage is 'iOS'"
reason: "the proper usage is 'iOS'"
},
},
iosnum: {
iosnum: {
expr: /\b(?:ios|iOs|ioS|IOS|Ios|IoS|ioS)([0-9]?)\b(\S|)(?!\S)/gm,
expr: /\b(?:ios|iOs|ioS|IOS|Ios|IoS|ioS)([0-9]?)\b(\S|)(?!\S)/gm,
replacement: "iOS $1$2",
replacement: "iOS $1$2",
reason: "the proper usage is 'iOS' followed by a space and the version number"
reason: "the proper usage is 'iOS' followed by a space and the version number"
},
},
yell: {
yell: {
expr: /^((?=.*[A-Z])[^a-z]*)$/g,
expr: /^((?=.*[A-Z])[^a-z]*)$/g,
replacement: "$1",
replacement: "$1",
reason: "no need to yell"
reason: "no need to yell"
},
},
wordpress: {
wordpress: {
expr: /[Ww]ordpress/g,
expr: /[Ww]ordpress/g,
replacement: "WordPress",
replacement: "WordPress",
reason: "'WordPress' is the proper capitalization"
reason: "'WordPress' is the proper capitalization"
},
},
google: {
google: {
expr: /(google)\b(\S|)(?!\S)/igm,
expr: /(google)\b(\S|)(?!\S)/igm,
replacement: "Google$2",
replacement: "Google$2",
reason: "Google is the proper capitalization"
reason: "Google is the proper capitalization"
},
},
mysql: {
mysql: {
expr: /(mysql)\b(\S|)(?!\S)/igm,
expr: /(mysql)\b(\S|)(?!\S)/igm,
replacement: "MySQL$2",
replacement: "MySQL$2",
reason: "MySQL is the proper capitalization"
reason: "MySQL is the proper capitalization"
},
},
apache: {
apache: {
expr: /(apache)\b(\S|)(?!\S)/igm,
expr: /(apache)\b(\S|)(?!\S)/igm,
replacement: "Apache$2",
replacement: "Apache$2",
reason: "Apache is the proper capitalization"
reason: "Apache is the proper capitalization"
},
},
git: {
git: {
expr: /(^|\s)(git|GIT)\b(\S|)(?!\S)/gm,
expr: /(^|\s)(git|GIT)\b(\S|)(?!\S)/gm,
replacement: "$1Git$3",
replacement: "$1Git$3",
reason: "Git is the proper capitalization"
reason: "Git is the proper capitalization"
},
},
harddisk: {
harddisk: {
expr: /(hdd|harddisk)\b(\S|)(?!\S)/igm,
expr: /(hdd|harddisk)\b(\S|)(?!\S)/igm,
replacement: "hard disk$2",
replacement: "hard disk$2",
reason: "Hard disk is the proper capitalization"
reason: "Hard disk is the proper capitalization"
},
},
github: {
github: {
expr: /\b([gG]ithub|GITHUB)\b(\S|)(?!\S)/gm,
expr: /\b([gG]ithub|GITHUB)\b(\S|)(?!\S)/gm,
replacement: "GitHub$2",
replacement: "GitHub$2",
reason: "GitHub is the proper capitalization"
reason: "GitHub is the proper capitalization"
},
},
facebook: {
facebook: {
expr: /\b([fF]acebook|FACEBOOK)\b(\S|)(?!\S)/gm,
expr: /\b([fF]acebook|FACEBOOK)\b(\S|)(?!\S)/gm,
replacement: "Facebook$2",
replacement: "Facebook$2",
reason: "Facebook is the proper capitalization"
reason: "Facebook is the proper capitalization"
},
},
python: {
python: {
expr: /(^|\s)[Pp]ython(.|$)/gm,
expr: /(^|\s)[Pp]ython(.|$)/gm,
replacement: "$1Python$2",
replacement: "$1Python$2",
reason: "'Python' is the proper capitalization"
reason: "'Python' is the proper capitalization"
},
},
im: {
im: {
expr: /(^|\s|\()im(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
expr: /(^|\s|\()im(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
replacement: "$1I'm$2",
replacement: "$1I'm$2",
reason: "in English, the personal pronoun is 'I'"
reason: "in English, the personal pronoun is 'I'"
},
},
ive: {
ive: {
expr: /(^|\s|\()ive(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
expr: /(^|\s|\()ive(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
replacement: "$1I've$2",
replacement: "$1I've$2",
reason: "in English, the personal pronoun is 'I'"
reason: "in English, the personal pronoun is 'I'"
},
},
ur: {
ur: {
expr: /(^|\s|\()ur(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
expr: /(^|\s|\()ur(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
replacement: "$1you are$2",
replacement: "$1you are$2",
reason: "de-text"
reason: "de-text"
},
},
u: {
u: {
expr: /(^|\s|\()u(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
expr: /(^|\s|\()u(\s|,|\.|!|\?|;|\/|\)|'|$)/gm,
replacement: "$1you$2",
replacement: "$1you$2",
reason: "de-text"
reason: "de-text"
},
},
allways: {
allways: {
expr: /(^|\s|\()(a)llways(\s|,|\.|!|\?|;|\/|\)|'|$)/gmi,
expr: /(^|\s|\()(a)llways(\s|,|\.|!|\?|;|\/|\)|'|$)/gmi,
replacement: "$1$2lways$3",
replacement: "$1$2lways$3",
reason: "spelling"
reason: "spelling"
},
},
appreciated: {
appreciated: {
expr: /(?:[\s-,']\w*)*(help|suggestion|advice).*(?:appreciated).*/gmi,
expr: /(?:[\s-,']\w*)*(help|suggestion|advice).*(?:appreciated).*/gmi,
replacement: "",
replacement: "",
reason: "$1 requests are unnecessary noise"
reason: "$1 requests are unnecessary noise"
},
},
hopeMaybeHelps: {
hopeMaybeHelps: {
expr: /(?:[\s-,']\w*)*(maybe|hope)+(?:[\s-,']\w*)*\s(help[s]*)(?:[\s-,']\w*)*[\.!?]/gmi,
expr: /(?:[\s-,']\w*)*(maybe|hope)+(?:[\s-,']\w*)*\s(help[s]*)(?:[\s-,']\w*)*[\.!?]/gmi,
replacement: "",
replacement: "",
reason: "$1...$2 is unnecessary noise"
reason: "$1...$2 is unnecessary noise"
},
},
regex: {
regex: {
expr: /regex(p)?/gmi,
expr: /regex(p)?/gmi,
replacement: function(match,p){ return "RegEx"+((p === undefined)?"":p).toLowerCase(); },
replacement: function(match,p){ return "RegEx"+((p === undefined)?"":p).toLowerCase(); },
reason: "RegEx or RegExp are the correct capitalizations"
reason: "RegEx or RegExp are the correct capitalizations"
},
},
multiplesymbols: {
multiplesymbols: {
expr: /\?\?+/gm,
expr: /\?\?+/gm,
replacement: "?",
replacement: "?",
reason: "One question mark for one question"
reason: "One question mark for one question"
},
},
// Whitespace compression comes last
// Whitespace compression comes last
multiplespaces: {
multiplespaces: {
expr: /(\S) +(\S)/gm,
expr: /(\S) +(\S)/gm,
replacement: "$1 $2",
replacement: "$1 $2",
reason: "One space at a time"
reason: "One space at a time"
},
},
spacesbeforepunctuation: {
spacesbeforepunctuation: {
复制
已复制
复制
已复制
expr: / +([.,:;?!])
/g,
//expr: / +([.,:;?!])/g,
expr: / +([.,:;?!])
[^\w]
/g,
replacement: "$1 ",
replacement: "$1 ",
reason: "punctuation & spacing"
reason: "punctuation & spacing"
},
},
spacesafterpunctuation: {
spacesafterpunctuation: {
expr: /([.,:;?!]) +/g,
expr: /([.,:;?!]) +/g,
replacement: "$1 ",
replacement: "$1 ",
reason: "punctuation & spacing"
reason: "punctuation & spacing"
},
},
leadingspace: {
leadingspace: {
expr: /^ +(\S)/gm,
expr: /^ +(\S)/gm,
replacement: "$1",
replacement: "$1",
reason: "punctuation & spacing"
reason: "punctuation & spacing"
},
},
blanklines: {
blanklines: {
expr: /(?:\s*[\r\n]){3,}/gm,
expr: /(?:\s*[\r\n]){3,}/gm,
replacement: "\n\n",
replacement: "\n\n",
reason: "punctuation & spacing"
reason: "punctuation & spacing"
},
},
endblanklines: {
endblanklines: {
expr: /[\s\r\n]+$/g,
expr: /[\s\r\n]+$/g,
replacement: "",
replacement: "",
reason: "punctuation & spacing"
reason: "punctuation & spacing"
}
}
};
};
// Populate funcs
// Populate funcs
App.popFuncs = function() {
App.popFuncs = function() {
// This is where the magic happens: this function takes a few pieces of information and applies edits to the post with a couple exceptions
// This is where the magic happens: this function takes a few pieces of information and applies edits to the post with a couple exceptions
App.funcs.fixIt = function(input, expression, replacement, reasoning) {
App.funcs.fixIt = function(input, expression, replacement, reasoning) {
// If there is nothing to search, exit
// If there is nothing to search, exit
if (!input) return false;
if (!input) return false;
// Scan the post text using the expression to see if there are any matches
// Scan the post text using the expression to see if there are any matches
var match = input.search(expression);
var match = input.search(expression);
// If so, increase the number of edits performed (used later for edit summary formation)
// If so, increase the number of edits performed (used later for edit summary formation)
if (match !== -1) {
if (match !== -1) {
// Later, this will store what is removed for the first case
// Later, this will store what is removed for the first case
var phrase;
var phrase;
// Then, perform the edits using replace()
// Then, perform the edits using replace()
// What follows is a series of exceptions, which I will explain below; I perform special actions by overriding replace()
// What follows is a series of exceptions, which I will explain below; I perform special actions by overriding replace()
// This is used for removing things entirely without giving a replacement; it matches the expression and then replaces it with nothing
// This is used for removing things entirely without giving a replacement; it matches the expression and then replaces it with nothing
if (replacement === "") {
if (replacement === "") {
var phrase2; // Hack on a hack - allow 2 replacements
var phrase2; // Hack on a hack - allow 2 replacements
input = input.replace(expression, function(data, match1, match2) {
input = input.replace(expression, function(data, match1, match2) {
// Save what is removed for the edit summary (see below)
// Save what is removed for the edit summary (see below)
phrase = match1;
phrase = match1;
phrase2 = match2;
phrase2 = match2;
// Replace with nothing
// Replace with nothing
return "";
return "";
});
});
// This is an interesting tidbit: if you want to make the edit summaries dynamic, you can keep track of a match that you receive
// This is an interesting tidbit: if you want to make the edit summaries dynamic, you can keep track of a match that you receive
// from overriding the replace() function and then use that in the summary
// from overriding the replace() function and then use that in the summary
reasoning = reasoning.replace("$1", phrase)
reasoning = reasoning.replace("$1", phrase)
.replace("$2", phrase2);
.replace("$2", phrase2);
// This allows me to combine the upvote and downvote replacement schemes into one
// This allows me to combine the upvote and downvote replacement schemes into one
} else if (replacement == "$1vote") {
} else if (replacement == "$1vote") {
input = input.replace(expression, function(data, match1) {
input = input.replace(expression, function(data, match1) {
phrase = match1;
phrase = match1;
return phrase + "vot";
return phrase + "vot";
});
});
reasoning = reasoning.replace("$1", phrase.toLowerCase());
reasoning = reasoning.replace("$1", phrase.toLowerCase());
// Fix all caps
// Fix all caps
} else if (reasoning === "no need to yell") {
} else if (reasoning === "no need to yell") {
input = input.replace(expression, function(data, match1) {
input = input.replace(expression, function(data, match1) {
return match1.substring(0, 1).toUpperCase() + match1.substring(1).toLowerCase();
return match1.substring(0, 1).toUpperCase() + match1.substring(1).toLowerCase();
});
});
// This is used to capitalize letters; it merely takes what is matched, uppercases it, and replaces what was matched with the uppercased version
// This is used to capitalize letters; it merely takes what is matched, uppercases it, and replaces what was matched with the uppercased version
} else if (replacement === "$1") {
} else if (replacement === "$1") {
input = input.replace(expression, function(data, match1) {
input = input.replace(expression, function(data, match1) {
return match1.toUpperCase();
return match1.toUpperCase();
});
});
// I can use C, C#, and C++ capitalization in one rule
// I can use C, C#, and C++ capitalization in one rule
} else if (replacement === "$1C$2") {
} else if (replacement === "$1C$2") {
var newPhrase;
var newPhrase;
input = input.replace(expression, function(data, match1, match2) {
input = input.replace(expression, function(data, match1, match2) {
newPhrase = match2;
newPhrase = match2;
return match1 + "C" + match2;
return match1 + "C" + match2;
});
});
reasoning = reasoning.replace("$2", newPhrase);
reasoning = reasoning.replace("$2", newPhrase);
// iOS numbering/spacing fixes
// iOS numbering/spacing fixes
} else if (replacement === "iOS $2") {
} else if (replacement === "iOS $2") {
input = input.replace(expression, function(data, match1) {
input = input.replace(expression, function(data, match1) {
if (match1.match(/\d/)) { // Is a number
if (match1.match(/\d/)) { // Is a number
return "iOS " + match1;
return "iOS " + match1;
}
}
return "iOS" + match1;
return "iOS" + match1;
});
});
// Default: just replace it with the indicated replacement
// Default: just replace it with the indicated replacement
} else {
} else {
input = input.replace(expression, replacement);
input = input.replace(expression, replacement);
}
}
// Return a dictionary with the reasoning for the fix and what is edited (used later to prevent duplicates in the edit summary)
// Return a dictionary with the reasoning for the fix and what is edited (used later to prevent duplicates in the edit summary)
return {
return {
reason: reasoning,
reason: reasoning,
fixed: input
fixed: input
};
};
} else {
} else {
// If nothing needs to be fixed, return null
// If nothing needs to be fixed, return null
return null;
return null;
}
}
};
};
// Omit code
// Omit code
App.funcs.omitCode = function(str, type) {
App.funcs.omitCode = function(str, type) {
str = str.replace(App.globals.checks[type], function(match) {
str = str.replace(App.globals.checks[type], function(match) {
App.globals.replacedStrings[type].push(match);
App.globals.replacedStrings[type].push(match);
return App.globals.placeHolders[type];
return App.globals.placeHolders[type];
});
});
return str;
return str;
};
};
// Replace code
// Replace code
App.funcs.replaceCode = function(str, type) {
App.funcs.replaceCode = function(str, type) {
for (var i = 0; i < App.globals.replacedStrings[type].length; i++) {
for (var i = 0; i < App.globals.replacedStrings[type].length; i++) {
str = str.replace(App.globals.placeHolders[type],
str = str.replace(App.globals.placeHolders[type],
App.globals.replacedStrings[type][i]);
App.globals.replacedStrings[type][i]);
}
}
return str;
return str;
};
};
复制
已复制
复制
已复制
// Eliminate duplicates in array (awesome method I found on SO, check it out!)
// From AstroCB: the original structure of the edit formation prevents duplicates.
// Unless you changed that structure somehow, this shouldn't be needed.
App.funcs.eliminateDuplicates = function(arr) {
var i, len = arr.length,
out = [],
obj = {};
for (i = 0; i < len; i++) {
obj[arr[i]] = 0;
}
for (i in obj) {
if (obj.hasOwnProperty(i)) { // Prevents messiness of for..in statements
out.push(i);
}
}
return out;
};
App.funcs.applyListeners = function() { // Removes default Stack Exchange listeners; see https://github.com/AstroCB/Stack-Exchange-Editor-Toolkit/issues/43
App.funcs.applyListeners = function() { // Removes default Stack Exchange listeners; see https://github.com/AstroCB/Stack-Exchange-Editor-Toolkit/issues/43
function removeEventListeners(e) {
function removeEventListeners(e) {
if (e.which === 13) {
if (e.which === 13) {
if (e.metaKey || e.ctrlKey) {
if (e.metaKey || e.ctrlKey) {
// CTRL/CMD + Enter -> Activate the auto-editor
// CTRL/CMD + Enter -> Activate the auto-editor
App.selections.buttonFix.click();
App.selections.buttonFix.click();
this.focus();
this.focus();
} else {
} else {
// It's impossible to remove the event listeners, so we have to clone the element without any listeners
// It's impossible to remove the event listeners, so we have to clone the element without any listeners
var elClone = this.cloneNode(true);
var elClone = this.cloneNode(true);
复制
已复制
复制
已复制
this.parentNode.replaceChild(elClone,
this);
this.parentNode.replaceChild(elClone,
this);
App.selections.submitButton.click();
App.selections.submitButton.click();
}
}
}
}
}
}
// Tags box
// Tags box
App.selections.tagField.keydown(removeEventListeners);
App.selections.tagField.keydown(removeEventListeners);
// Edit summary box
// Edit summary box
App.selections.summaryBox.keydown(removeEventListeners);
App.selections.summaryBox.keydown(removeEventListeners);
};
};
// Wait for relevant dynamic content to finish loading
// Wait for relevant dynamic content to finish loading
App.funcs.dynamicDelay = function(callback) {
App.funcs.dynamicDelay = function(callback) {
复制
已复制
复制
已复制
setTimeout(function() {
if (App.globals.inline) { // Inline editing
App.selections.buttonBar = $('#wmd-button-bar-' + App.globals.targetID);
App.selections.buttonBar.unbind();
setTimeout(function() {
setTimeout(function() {
复制
已复制
复制
已复制
callback();
App.selections.buttonBar = $('#wmd-button-bar-' + App.globals.targetID);
}, 0);
App.selections.buttonBar.unbind();
}, 500);
setTimeout(function() {
};
callback();
}, 0);
// Populate or refresh DOM selections
}, 500);
App.funcs.popSelections = function() {
} else { // Question page editing
var targetID = App.globals.targetID;
App.selections.
var scope = $('div[data-questionid="' + targetID + '"]');
if (!scope.length) scope = $('div[data-answerid="' + targetID + '"]');
if (!scope.length) scope = '';
App.selections.redoButton = $('#wmd-redo-button-' + targetID, scope);
App.selections.bodyBox = $("#wmd-input-" + targetID, scope);
App.selections.titleBox = $('[class*="title-field"]', scope);
App.selections.summaryBox = $("#edit-comment-" + targetID, scope);
App.selections.tagField = $($(".tag-editor")[0], scope);
App.selections.submitButton = $("#submit-button-" + targetID, scope);
};
// Populate edit item sets from DOM selections
App.funcs.popItems = function() {
App.items[0] = {
title: String(
App.selections.
titleBox.v
已保存差异
原始文本
打开文件
// ==UserScript== // @name Stack-Exchange-Editor-Toolkit // @author Cameron Bernhardt (AstroCB) // @developer Jonathan Todd (jt0dd) // @developer sathyabhat // @contributor Unihedron // @contributor Tiny Giant // @contributor Mogsdad // @grant none // @license MIT // @namespace http://github.com/AstroCB // @version 1.5.2.4 // @description Fix common grammar/usage annoyances on Stack Exchange posts with a click // @include *://*.stackexchange.com/questions/* // @include *://stackoverflow.com/questions/* // @include *://stackoverflow.com/review/helper/* // @include *://meta.stackoverflow.com/questions/* // @include *://serverfault.com/questions/* // @include *://meta.serverfault.com/questions/* // @include *://superuser.com/questions/* // @include *://meta.superuser.com/questions/* // @include *://askubuntu.com/questions/* // @include *://meta.askubuntu.com/questions/* // @include *://stackapps.com/questions/* // @include *://*.stackexchange.com/posts/* // @include *://stackoverflow.com/posts/* // @include *://meta.stackoverflow.com/posts/* // @include *://serverfault.com/posts/* // @include *://meta.serverfault.com/posts/* // @include *://superuser.com/posts/* // @include *://meta.superuser.com/posts/* // @include *://askubuntu.com/posts/* // @include *://meta.askubuntu.com/posts/* // @include *://stackapps.com/posts/* // @include *://*.stackexchange.com/review/* // @include *://stackoverflow.com/review/* // @include *://meta.stackoverflow.com/review/* // @include *://serverfault.com/review/* // @include *://meta.serverfault.com/review/* // @include *://superuser.com/review/* // @include *://meta.superuser.com/review/* // @include *://askubuntu.com/review/* // @include *://meta.askubuntu.com/review/* // @include *://stackapps.com/review/* // @exclude *://*.stackexchange.com/questions/tagged/* // @exclude *://stackoverflow.com/questions/tagged/* // @exclude *://meta.stackoverflow.com/questions/tagged/* // @exclude *://serverfault.com/questions/tagged/* // @exclude *://meta.serverfault.com/questions/* // @exclude *://superuser.com/questions/tagged/* // @exclude *://meta.superuser.com/questions/tagged/* // @exclude *://askubuntu.com/questions/tagged/* // @exclude *://meta.askubuntu.com/questions/tagged/* // @exclude *://stackapps.com/questions/tagged/* // ==/UserScript== var main = function() { // Define app namespace function EditorToolkit(targetID) { if (!(this instanceof EditorToolkit)) return false; var App = this; // Place edit items here App.items = []; // Place selected jQuery items here App.selections = {}; // Place "global" app data here App.globals = {}; // Place "helper" functions here App.funcs = {}; //Preload icon alt var SEETicon = new Image(); SEETicon.src = '//i.imgur.com/d5ZL09o.png'; // Define variables for later use App.globals.barReady = false; App.globals.editsMade = false; App.globals.editCount = 0; App.globals.infoContent = ''; // Check if there was an ID passed (if not, use question ID from URL); if (!targetID) targetID = window.location.href.match(/\/(\d+)\//g)[0].split("/").join(""); App.globals.targetID = targetID; App.globals.spacerHTML = '<li class="wmd-spacer wmd-spacer3" id="wmd-spacer3-' + App.globals.targetID + '" style="left: 400px !important;"></li>'; App.selections.buttonWrapper = $('<div class="ToolkitButtonWrapper"/>'); App.selections.buttonFix = $('<button class="wmd-button ToolkitFix" />'); App.selections.buttonInfo = $('<div class="ToolkitInfo">'); App.selections.buttonWrapper.append(App.selections.buttonFix); App.selections.buttonWrapper.append(App.selections.buttonInfo); App.globals.reasons = []; App.globals.numReasons = 0; App.globals.replacedStrings = { "block": [], "inline": [] }; App.globals.placeHolders = { "block": "_xCodexBlockxPlacexHolderx_", "inline": "_xCodexInlinexPlacexHolderx_" }; App.globals.checks = { "block": /( )+.*/gm, "inline": /`.*`/gm }; // Assign modules here App.globals.pipeMods = {}; // Define order in which mods affect here App.globals.order = ["omit", "edit", "replace"]; // Define edit rules App.edits = { i: { expr: /(^|\s|\()i(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1I$2", reason: "in English, the personal pronoun is 'I'" }, so: { expr: /(^|\s)[Ss]tack\s*overflow|StackOverflow(.|$)/gm, replacement: "$1Stack Overflow$2", reason: "'Stack Overflow' is the legal name" }, se: { expr: /(^|\s)[Ss]tack\s*exchange|StackExchange(.|$)/gm, replacement: "$1Stack Exchange$2", reason: "'Stack Exchange' is the legal name" }, expansionSO: { expr: /(^|\s)SO(\s|,|\.|!|\?|;|\/|\)|$)/gm, replacement: "$1Stack Overflow$2", reason: "'SO' expansion" }, expansionSE: { expr: /(^|\s)SE(\s|,|\.|!|\?|;|\/|\)|$)/gm, replacement: "$1Stack Exchange$2", reason: "'SE' expansion" }, javascript: { expr: /(^|\s)[Jj]ava\s*[Ss]cript(.|$)/gm, replacement: "$1JavaScript$2", reason: "'JavaScript' is the proper capitalization" }, jsfiddle: { expr: /(^|\s)[Jj][Ss]\s*[Ff]iddle(.|$)/gm, replacement: "$1JSFiddle$2", reason: "'JSFiddle' is the currently accepted capitalization" }, caps: { expr: /^(?!https?)([a-z])/gm, replacement: "$1", reason: "copy edited" }, jquery: { expr: /(^|\s)[Jj][Qq]uery(.|$)/gm, replacement: "$1jQuery$2", reason: "'jQuery' is the proper capitalization" }, html: { expr: /(^|\s)[Hh]tml([5]?)\b(\S|)(?!\S)/gm, replacement: "$1HTML$2$3", reason: "HTML stands for HyperText Markup Language" }, css: { expr: /(^|\s)[Cc]ss\b(\S|)(?!\S)/gm, replacement: "$1CSS$2", reason: "CSS stands for Cascading Style Sheets" }, json: { expr: /(^|\s)[Jj]son\b(\S|)(?!\S)/gm, replacement: "$1JSON$2", reason: "JSON stands for JavaScript Object Notation" }, ajax: { expr: /(^|\s)ajax\b(\S|)(?!\S)/gm, replacement: "$1AJAX$2", reason: "AJAX stands for Asynchronous JavaScript and XML" }, angular: { expr: /[Aa]ngular[Jj][Ss]/g, replacement: "AngularJS", reason: "'AngularJS is the proper capitalization" }, thanks: { expr: /(thanks|pl(?:ease|z|s)\s+h[ea]lp|cheers|regards|thx|thank\s+you|my\s+first\s+question|kindly\shelp).*$/gmi, replacement: "", reason: "'$1' is unnecessary noise" }, commas: { expr: /,([^\s])/g, replacement: ", $1", reason: "punctuation & spacing" }, php: { expr: /(^|\s)[Pp]hp\b(\S|)(?!\S)/gm, replacement: "$1PHP$2", reason: "PHP stands for PHP: Hypertext Preprocessor" }, hello: { expr: /(?:^|\s)(hi\s+guys|hi|hello|good\s(?:evening|morning|day|afternoon))(?:\.|!|\ )/gmi, replacement: "", reason: "greetings like '$1' are unnecessary noise" }, edit: { expr: /(?:^\**)(edit|update):?(?:\**):?/gmi, replacement: "", reason: "Stack Exchange has an advanced revision history system: 'Edit' or 'Update' is unnecessary" }, voting: { expr: /([Dd]own|[Uu]p)[\s*\-]vot/g, replacement: "$1vote", reason: "the proper spelling (despite the tag name) is '$1vote' (one word)" }, mysite: { expr: /mysite\./g, replacement: "example.", reason: "links to mysite.domain are not allowed: use example.domain instead" }, c: { expr: /(^|\s)c(#|\++|\s|$)/gm, replacement: "$1C$2", reason: "C$2 is the proper capitalization" }, java: { expr: /(^|\s)java\b(\S|)(?!\S)/gmi, replacement: "$1Java$2", reason: "Java should be capitalized" }, sql: { expr: /(^|\s)[Ss]ql\b(\S|)(?!\S)/gm, replacement: "$1SQL$2", reason: "SQL is the proper capitalization" }, sqlite: { expr: /(^|\s)[Ss]qlite([0-9]*)\b(\S|)(?!\S)/gm, replacement: "$1SQLite$2$3", reason: "SQLite is the proper capitalization" }, android: { expr: /(^|\s)android\b(\S|)(?!\S)/gmi, replacement: "$1Android$2", reason: "Android should be capitalized" }, oracle: { expr: /(^|\s)oracle\b(\S|)(?!\S)/gmi, replacement: "$1Oracle$2", reason: "Oracle should be capitalized" }, windows: { expr: /(win|windows(?:\ ?)(\s[0-9]+))\b(\S|)(?!\S)/igm, replacement: "Windows$2$3", reason: "Windows should be capitalized" }, windowsXP: { expr: /(win|windows(?:\ ?)(\sxp))\b(\S|)(?!\S)/igm, replacement: "Windows XP$3", reason: "Windows XP should be capitalized" }, windowsVista: { expr: /(win|windows(?:\ ?)(\svista))\b(\S|)(?!\S)/igm, replacement: "Windows Vista$3", reason: "Windows Vista should be capitalized" }, ubuntu: { expr: /(ubunto|ubunut|ubunutu|ubunu|ubntu|ubutnu|ubanto[o]+|unbuntu|ubunt|ubutu)\b(\S|)(?!\S)/igm, replacement: "Ubuntu$2", reason: "corrected Ubuntu spelling" }, linux: { expr: /(linux)\b(\S|)(?!\S)/igm, replacement: "Linux$2", reason: "Linux should be capitalized" }, apostrophes: { expr: /(^|\s)(can|doesn|don|won|hasn|isn|didn)t(\s|$)/gmi, replacement: "$1$2't$3", reason: "English contractions use apostrophes" }, ios: { expr: /\b(?:ios|iOs|ioS|IOS|Ios|IoS|ioS)\b(\S|)(?!\S)/gm, replacement: "iOS$1", reason: "the proper usage is 'iOS'" }, iosnum: { expr: /\b(?:ios|iOs|ioS|IOS|Ios|IoS|ioS)([0-9]?)\b(\S|)(?!\S)/gm, replacement: "iOS $1$2", reason: "the proper usage is 'iOS' followed by a space and the version number" }, yell: { expr: /^((?=.*[A-Z])[^a-z]*)$/g, replacement: "$1", reason: "no need to yell" }, wordpress: { expr: /[Ww]ordpress/g, replacement: "WordPress", reason: "'WordPress' is the proper capitalization" }, google: { expr: /(google)\b(\S|)(?!\S)/igm, replacement: "Google$2", reason: "Google is the proper capitalization" }, mysql: { expr: /(mysql)\b(\S|)(?!\S)/igm, replacement: "MySQL$2", reason: "MySQL is the proper capitalization" }, apache: { expr: /(apache)\b(\S|)(?!\S)/igm, replacement: "Apache$2", reason: "Apache is the proper capitalization" }, git: { expr: /(^|\s)(git|GIT)\b(\S|)(?!\S)/gm, replacement: "$1Git$3", reason: "Git is the proper capitalization" }, harddisk: { expr: /(hdd|harddisk)\b(\S|)(?!\S)/igm, replacement: "hard disk$2", reason: "Hard disk is the proper capitalization" }, github: { expr: /\b([gG]ithub|GITHUB)\b(\S|)(?!\S)/gm, replacement: "GitHub$2", reason: "GitHub is the proper capitalization" }, facebook: { expr: /\b([fF]acebook|FACEBOOK)\b(\S|)(?!\S)/gm, replacement: "Facebook$2", reason: "Facebook is the proper capitalization" }, python: { expr: /(^|\s)[Pp]ython(.|$)/gm, replacement: "$1Python$2", reason: "'Python' is the proper capitalization" }, im: { expr: /(^|\s|\()im(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1I'm$2", reason: "in English, the personal pronoun is 'I'" }, ive: { expr: /(^|\s|\()ive(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1I've$2", reason: "in English, the personal pronoun is 'I'" }, ur: { expr: /(^|\s|\()ur(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1you are$2", reason: "de-text" }, u: { expr: /(^|\s|\()u(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1you$2", reason: "de-text" }, allways: { expr: /(^|\s|\()(a)llways(\s|,|\.|!|\?|;|\/|\)|'|$)/gmi, replacement: "$1$2lways$3", reason: "spelling" }, appreciated: { expr: /(?:[\s-,']\w*)*(help|suggestion|advice).*(?:appreciated).*/gmi, replacement: "", reason: "$1 requests are unnecessary noise" }, hopeMaybeHelps: { expr: /(?:[\s-,']\w*)*(maybe|hope)+(?:[\s-,']\w*)*\s(help[s]*)(?:[\s-,']\w*)*[\.!?]/gmi, replacement: "", reason: "$1...$2 is unnecessary noise" }, regex: { expr: /regex(p)?/gmi, replacement: function(match,p){ return "RegEx"+((p === undefined)?"":p).toLowerCase(); }, reason: "RegEx or RegExp are the correct capitalizations" }, multiplesymbols: { expr: /\?\?+/gm, replacement: "?", reason: "One question mark for one question" }, // Whitespace compression comes last multiplespaces: { expr: /(\S) +(\S)/gm, replacement: "$1 $2", reason: "One space at a time" }, spacesbeforepunctuation: { expr: / +([.,:;?!])/g, replacement: "$1 ", reason: "punctuation & spacing" }, spacesafterpunctuation: { expr: /([.,:;?!]) +/g, replacement: "$1 ", reason: "punctuation & spacing" }, leadingspace: { expr: /^ +(\S)/gm, replacement: "$1", reason: "punctuation & spacing" }, blanklines: { expr: /(?:\s*[\r\n]){3,}/gm, replacement: "\n\n", reason: "punctuation & spacing" }, endblanklines: { expr: /[\s\r\n]+$/g, replacement: "", reason: "punctuation & spacing" } }; // Populate funcs App.popFuncs = function() { // This is where the magic happens: this function takes a few pieces of information and applies edits to the post with a couple exceptions App.funcs.fixIt = function(input, expression, replacement, reasoning) { // 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 match = input.search(expression); // If so, increase the number of edits performed (used later for edit summary formation) if (match !== -1) { // Later, this will store what is removed for the first case var phrase; // Then, perform the edits using replace() // What follows is a series of exceptions, which I will explain below; I perform special actions by overriding replace() // This is used for removing things entirely without giving a replacement; it matches the expression and then replaces it with nothing if (replacement === "") { var phrase2; // Hack on a hack - allow 2 replacements input = input.replace(expression, function(data, match1, match2) { // Save what is removed for the edit summary (see below) phrase = match1; phrase2 = match2; // Replace with nothing return ""; }); // This is an interesting tidbit: if you want to make the edit summaries dynamic, you can keep track of a match that you receive // from overriding the replace() function and then use that in the summary reasoning = reasoning.replace("$1", phrase) .replace("$2", phrase2); // This allows me to combine the upvote and downvote replacement schemes into one } else if (replacement == "$1vote") { input = input.replace(expression, function(data, match1) { phrase = match1; return phrase + "vot"; }); reasoning = reasoning.replace("$1", phrase.toLowerCase()); // Fix all caps } else if (reasoning === "no need to yell") { input = input.replace(expression, function(data, match1) { return match1.substring(0, 1).toUpperCase() + match1.substring(1).toLowerCase(); }); // This is used to capitalize letters; it merely takes what is matched, uppercases it, and replaces what was matched with the uppercased version } else if (replacement === "$1") { input = input.replace(expression, function(data, match1) { return match1.toUpperCase(); }); // I can use C, C#, and C++ capitalization in one rule } else if (replacement === "$1C$2") { var newPhrase; input = input.replace(expression, function(data, match1, match2) { newPhrase = match2; return match1 + "C" + match2; }); reasoning = reasoning.replace("$2", newPhrase); // iOS numbering/spacing fixes } else if (replacement === "iOS $2") { input = input.replace(expression, function(data, match1) { if (match1.match(/\d/)) { // Is a number return "iOS " + match1; } return "iOS" + match1; }); // Default: just replace it with the indicated replacement } else { input = input.replace(expression, replacement); } // Return a dictionary with the reasoning for the fix and what is edited (used later to prevent duplicates in the edit summary) return { reason: reasoning, fixed: input }; } else { // If nothing needs to be fixed, return null return null; } }; // Omit code App.funcs.omitCode = function(str, type) { str = str.replace(App.globals.checks[type], function(match) { App.globals.replacedStrings[type].push(match); return App.globals.placeHolders[type]; }); return str; }; // Replace code App.funcs.replaceCode = function(str, type) { for (var i = 0; i < App.globals.replacedStrings[type].length; i++) { str = str.replace(App.globals.placeHolders[type], App.globals.replacedStrings[type][i]); } return str; }; App.funcs.applyListeners = function() { // Removes default Stack Exchange listeners; see https://github.com/AstroCB/Stack-Exchange-Editor-Toolkit/issues/43 function removeEventListeners(e) { if (e.which === 13) { if (e.metaKey || e.ctrlKey) { // CTRL/CMD + Enter -> Activate the auto-editor App.selections.buttonFix.click(); this.focus(); } else { // It's impossible to remove the event listeners, so we have to clone the element without any listeners var elClone = this.cloneNode(true); this.parentNode.replaceChild(elClone, this); App.selections.submitButton.click(); } } } // Tags box App.selections.tagField.keydown(removeEventListeners); // Edit summary box App.selections.summaryBox.keydown(removeEventListeners); }; // Wait for relevant dynamic content to finish loading App.funcs.dynamicDelay = function(callback) { setTimeout(function() { App.selections.buttonBar = $('#wmd-button-bar-' + App.globals.targetID); App.selections.buttonBar.unbind(); setTimeout(function() { callback(); }, 0); }, 500); }; // Populate or refresh DOM selections App.funcs.popSelections = function() { var targetID = App.globals.targetID; var scope = $('div[data-questionid="' + targetID + '"]'); if (!scope.length) scope = $('div[data-answerid="' + targetID + '"]'); if (!scope.length) scope = ''; App.selections.redoButton = $('#wmd-redo-button-' + targetID, scope); App.selections.bodyBox = $("#wmd-input-" + targetID, scope); App.selections.titleBox = $('[class*="title-field"]', scope); App.selections.summaryBox = $("#edit-comment-" + targetID, scope); App.selections.tagField = $($(".tag-editor")[0], scope); App.selections.submitButton = $("#submit-button-" + targetID, scope); }; // Populate edit item sets from DOM selections App.funcs.popItems = function() { App.items[0] = { title: String(App.selections.titleBox.val()).trim(), body: String(App.selections.bodyBox.val()).trim(), summary: String(App.selections.summaryBox.val()).trim() }; }; // Insert editing button(s) App.funcs.createButton = function() { // Insert button App.selections.redoButton.after(App.selections.buttonWrapper); // Insert spacer App.selections.redoButton.after(App.globals.spacerHTML); }; // Style button App.funcs.styleButton = function() { var buttonCSS = { 'position': 'relative', 'left': '430px', 'padding-top': '2%' }; $("#wmd-help-button-" + App.globals.targetID).css({ 'padding': '0px' }); App.selections.buttonWrapper.css(buttonCSS); App.selections.buttonFix.css({ 'position': 'static', 'float': 'left', 'border-width': '0px', 'background-color': 'white', 'background-image': 'url("//i.imgur.com/79qYzkQ.png")', 'background-size': '100% 100%', 'width': '18px', 'height': '18px', 'outline': 'none', 'box-shadow': 'none' }); App.selections.buttonInfo.css({ 'position': 'static', 'float': 'left', 'margin-left': '5px', 'font-size': '12px', 'color': '#424242', 'line-height': '19px' }); App.selections.buttonFix.hover(function() { App.globals.infoContent = App.selections.buttonInfo.text(); App.selections.buttonInfo.text('Fix the content!'); App.selections.buttonFix.css({ 'background-image': 'url("//i.imgur.com/d5ZL09o.png")' }); }, function() { App.selections.buttonInfo.text(App.globals.infoContent); App.selections.buttonFix.css({ 'background-image': 'url("//i.imgur.com/79qYzkQ.png")' }); }); }; // Listen to button click App.funcs.listenButton = function() { App.selections.buttonFix.click(function(e) { e.preventDefault(); // Refresh item population App.funcs.popItems(); // Pipe data through editing modules App.pipe(App.items, App.globals.pipeMods, App.globals.order); }); }; // Figure out the last selected element before pressing the button so we can return there after focusing the summary field App.funcs.setLastFocus = function() { App.selections.titleBox.click(function() { App.globals.lastSelectedElement = $(this); }); App.selections.bodyBox.click(function() { App.globals.lastSelectedElement = $(this); }); App.selections.summaryBox.click(function() { App.globals.lastSelectedElement = $(this); }); App.selections.tagField.click(function() { App.globals.lastSelectedElement = $(this); }); }; // Handle pipe output App.funcs.output = function(data) { App.selections.titleBox.val(data[0].title); App.selections.bodyBox.val(data[0].body); App.selections.summaryBox.val(data[0].summary); // Update the comment: focusing on the input field to remove placeholder text, but scroll back to the user's original location App.globals.currentPos = document.body.scrollTop; if ($("#wmd-input")) { $("#wmd-input").focus(); $("#edit-comment").focus(); $("#wmd-input").focus(); } else { $(".wmd-input")[0].focus(); $(".edit-comment")[0].focus(); $(".wmd-input")[0].focus(); } window.scrollTo(0, App.globals.currentPos); App.globals.infoContent = App.globals.reasons.length + ' changes made'; App.selections.buttonInfo.text(App.globals.infoContent); }; }; // 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]; data = mods[modName](data); } } App.funcs.output(data); }; // Init app App.init = function() { App.popFuncs(); App.funcs.dynamicDelay(function() { App.funcs.popSelections(); App.funcs.createButton(); App.funcs.styleButton(); App.funcs.popItems(); App.funcs.listenButton(); App.funcs.applyListeners(); App.funcs.setLastFocus(); }); }; App.globals.pipeMods.omit = function(data) { data[0].body = App.funcs.omitCode(data[0].body, "block"); data[0].body = App.funcs.omitCode(data[0].body, "inline"); return data; }; App.globals.pipeMods.replace = function(data) { data[0].body = App.funcs.replaceCode(data[0].body, "block"); data[0].body = App.funcs.replaceCode(data[0].body, "inline"); return data; }; App.globals.pipeMods.edit = function(data) { // Visually confirm edit - SE makes it easy because the jQuery color animation plugin seems to be there by default App.selections.bodyBox.animate({ backgroundColor: '#c8ffa7' }, 10); App.selections.bodyBox.animate({ backgroundColor: '#fff' }, 1000); // Loop through all editing rules for (var j in App.edits) { if (App.edits.hasOwnProperty(j)) { // Check body var fix = App.funcs.fixIt(data[0].body, App.edits[j].expr, App.edits[j].replacement, App.edits[j].reason); if (fix) { App.globals.reasons[App.globals.reasons.length] = fix.reason; data[0].body = fix.fixed; App.edits[j].fixed = true; } // Check title fix = App.funcs.fixIt(data[0].title, App.edits[j].expr, App.edits[j].replacement, App.edits[j].reason); if (fix) { data[0].title = fix.fixed; if (!App.edits[j].fixed) { App.globals.reasons[App.globals.reasons.length] = fix.reason; App.edits[j].fixed = true; } } } } // If there are no reasons, exit if(!App.globals.reasons.length) return false; // We want to store the generated summary separately so we can check if any changes have been made to it. // This is so we can click the button multiple times. I know, I know but it is a UX thing. var tmpSummary = ''; for (var z = App.globals.reasons.length - 1, x = 0; z >= 0; --z) { // Check that summary is not getting too long if (data[0].summary.length + tmpSummary.length + App.globals.reasons[z].length + 2 > 300) break; // If the reason already exists, skip it if (data[0].summary.indexOf(App.globals.reasons[z]) !== -1) continue; // Capitalize first letter if (x === 0) App.globals.reasons[z] = App.globals.reasons[z][0].toUpperCase() + App.globals.reasons[z].substring(1); // If the reason already exists, skip it if (data[0].summary.indexOf(App.globals.reasons[z]) !== -1) continue; // Append the reason and a semicolon (or period if it is the last reason) to the summary tmpSummary += App.globals.reasons[z] + (z === 0 ? "." : "; "); ++x; } // If no reasons have been applied, exit if (!tmpSummary) return false; // Store the summary for readability var summary = data[0].summary; // This whole ternary mess is for if the summary is not empty, and if this is the first time around or not data[0].summary = (summary ? (summary.substr(-1) !== -1 ? summary.substr(0,summary.length-1) : summary) + '; ' : '') + tmpSummary; // Focus the summary field App.selections.summaryBox.focus(); return data; }; App.init(); } var Apps = []; // It will be this if you are in the queue var targetID = $('.post-id').text(); var selector = '.edit-post, [value*="Edit"]:not([value="Save Edits"])'; var clickables = $(selector); if (clickables.length) { clickables.click(function(e) { if(e.target.href) targetID = e.target.href.match(/\d/g).join(""); Apps[targetID] = new EditorToolkit(targetID); }); } else Apps[$('#post-id').val()] = new EditorToolkit($('#post-id').val()); }; // Inject the main script var script = document.createElement('script'); script.type = "text/javascript"; script.textContent = '(' + main.toString() + ')();'; document.body.appendChild(script);
更改后文本
打开文件
// ==UserScript== // @name Stack-Exchange-Editor-Toolkit // @author Cameron Bernhardt (AstroCB) // @developer Jonathan Todd (jt0dd) // @developer sathyabhat // @contributor Unihedron // @contributor Tiny Giant // @contributor Mogsdad // @grant none // @license MIT // @namespace http://github.com/AstroCB // @version 1.5.2.5 // @description Fix common grammar/usage annoyances on Stack Exchange posts with a click // @include *://*.stackexchange.com/questions/* // @include *://stackoverflow.com/questions/* // @include *://stackoverflow.com/review/helper/* // @include *://meta.stackoverflow.com/questions/* // @include *://serverfault.com/questions/* // @include *://meta.serverfault.com/questions/* // @include *://superuser.com/questions/* // @include *://meta.superuser.com/questions/* // @include *://askubuntu.com/questions/* // @include *://meta.askubuntu.com/questions/* // @include *://stackapps.com/questions/* // @include *://*.stackexchange.com/posts/* // @include *://stackoverflow.com/posts/* // @include *://meta.stackoverflow.com/posts/* // @include *://serverfault.com/posts/* // @include *://meta.serverfault.com/posts/* // @include *://superuser.com/posts/* // @include *://meta.superuser.com/posts/* // @include *://askubuntu.com/posts/* // @include *://meta.askubuntu.com/posts/* // @include *://stackapps.com/posts/* // @include *://*.stackexchange.com/review/* // @include *://stackoverflow.com/review/* // @include *://meta.stackoverflow.com/review/* // @include *://serverfault.com/review/* // @include *://meta.serverfault.com/review/* // @include *://superuser.com/review/* // @include *://meta.superuser.com/review/* // @include *://askubuntu.com/review/* // @include *://meta.askubuntu.com/review/* // @include *://stackapps.com/review/* // @exclude *://*.stackexchange.com/questions/tagged/* // @exclude *://stackoverflow.com/questions/tagged/* // @exclude *://meta.stackoverflow.com/questions/tagged/* // @exclude *://serverfault.com/questions/tagged/* // @exclude *://meta.serverfault.com/questions/* // @exclude *://superuser.com/questions/tagged/* // @exclude *://meta.superuser.com/questions/tagged/* // @exclude *://askubuntu.com/questions/tagged/* // @exclude *://meta.askubuntu.com/questions/tagged/* // @exclude *://stackapps.com/questions/tagged/* // ==/UserScript== var main = function() { // Define app namespace function EditorToolkit(inline, targetID) { if (!(this instanceof EditorToolkit)) return false; var App = this; // Place edit items here App.items = []; // Place selected jQuery items here App.selections = {}; // Place "global" app data here App.globals = {}; // Place "helper" functions here App.funcs = {}; //Preload icon alt var SEETicon = new Image(); SEETicon.src = '//i.imgur.com/d5ZL09o.png'; // Define variables for later use App.globals.barReady = false; App.globals.editsMade = false; App.globals.editCount = 0; App.globals.infoContent = ''; // Check if there was an ID passed (if not, use question ID from URL); if (!targetID) targetID = window.location.href.match(/\/(\d+)\//g)[0].split("/").join(""); App.globals.targetID = targetID; App.globals.inline = inline; App.globals.spacerHTML = '<li class="wmd-spacer wmd-spacer3" id="wmd-spacer3-' + App.globals.targetID + '" style="left: 400px !important;"></li>'; App.selections.buttonWrapper = $('<div class="ToolkitButtonWrapper"/>'); App.selections.buttonFix = $('<button class="wmd-button ToolkitFix" />'); App.selections.buttonInfo = $('<div class="ToolkitInfo">'); App.selections.buttonWrapper.append(App.selections.buttonFix); App.selections.buttonWrapper.append(App.selections.buttonInfo); App.globals.reasons = []; App.globals.numReasons = 0; App.globals.replacedStrings = { "block": [], "inline": [] }; App.globals.placeHolders = { "block": "_xCodexBlockxPlacexHolderx_", "inline": "_xCodexInlinexPlacexHolderx_" }; App.globals.checks = { "block": /( )+.*/gm, "inline": /`.*`/gm }; // Assign modules here App.globals.pipeMods = {}; // Define order in which mods affect here App.globals.order = ["omit", "edit", "replace"]; // Define edit rules App.edits = { i: { expr: /(^|\s|\()i(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1I$2", reason: "in English, the personal pronoun is 'I'" }, so: { expr: /(^|\s)[Ss]tack\s*overflow|StackOverflow(.|$)/gm, replacement: "$1Stack Overflow$2", reason: "'Stack Overflow' is the legal name" }, se: { expr: /(^|\s)[Ss]tack\s*exchange|StackExchange(.|$)/gm, replacement: "$1Stack Exchange$2", reason: "'Stack Exchange' is the legal name" }, expansionSO: { expr: /(^|\s)SO(\s|,|\.|!|\?|;|\/|\)|$)/gm, replacement: "$1Stack Overflow$2", reason: "'SO' expansion" }, expansionSE: { expr: /(^|\s)SE(\s|,|\.|!|\?|;|\/|\)|$)/gm, replacement: "$1Stack Exchange$2", reason: "'SE' expansion" }, javascript: { expr: /(^|\s)[Jj]ava\s*[Ss]cript(.|$)/gm, replacement: "$1JavaScript$2", reason: "'JavaScript' is the proper capitalization" }, jsfiddle: { expr: /(^|\s)[Jj][Ss]\s*[Ff]iddle(.|$)/gm, replacement: "$1JSFiddle$2", reason: "'JSFiddle' is the currently accepted capitalization" }, caps: { expr: /^(?!https?)([a-z])/gm, replacement: "$1", reason: "copy edited" }, jquery: { expr: /(^|\s)[Jj][Qq]uery(.|$)/gm, replacement: "$1jQuery$2", reason: "'jQuery' is the proper capitalization" }, html: { expr: /(^|\s)[Hh]tml([5]?)\b(\S|)(?!\S)/gm, replacement: "$1HTML$2$3", reason: "HTML stands for HyperText Markup Language" }, css: { expr: /(^|\s)[Cc]ss\b(\S|)(?!\S)/gm, replacement: "$1CSS$2", reason: "CSS stands for Cascading Style Sheets" }, json: { expr: /(^|\s)[Jj]son\b(\S|)(?!\S)/gm, replacement: "$1JSON$2", reason: "JSON stands for JavaScript Object Notation" }, ajax: { expr: /(^|\s)ajax\b(\S|)(?!\S)/gm, replacement: "$1AJAX$2", reason: "AJAX stands for Asynchronous JavaScript and XML" }, angular: { expr: /[Aa]ngular[Jj][Ss]/g, replacement: "AngularJS", reason: "'AngularJS is the proper capitalization" }, thanks: { expr: /(thanks|pl(?:ease|z|s)\s+h[ea]lp|cheers|regards|thx|thank\s+you|my\s+first\s+question|kindly\shelp).*$/gmi, replacement: "", reason: "'$1' is unnecessary noise" }, commas: { expr: /,([^\s])/g, replacement: ", $1", reason: "punctuation & spacing" }, php: { expr: /(^|\s)[Pp]hp\b(\S|)(?!\S)/gm, replacement: "$1PHP$2", reason: "PHP stands for PHP: Hypertext Preprocessor" }, hello: { expr: /(?:^|\s)(hi\s+guys|hi|hello|good\s(?:evening|morning|day|afternoon))(?:\.|!|\ )/gmi, replacement: "", reason: "greetings like '$1' are unnecessary noise" }, edit: { expr: /(?:^\**)(edit|update):?(?:\**):?/gmi, replacement: "", reason: "Stack Exchange has an advanced revision history system: 'Edit' or 'Update' is unnecessary" }, voting: { expr: /([Dd]own|[Uu]p)[\s*\-]vot/g, replacement: "$1vote", reason: "the proper spelling (despite the tag name) is '$1vote' (one word)" }, mysite: { expr: /mysite\./g, replacement: "example.", reason: "links to mysite.domain are not allowed: use example.domain instead" }, c: { expr: /(^|\s)c(#|\++|\s|$)/gm, replacement: "$1C$2", reason: "C$2 is the proper capitalization" }, java: { expr: /(^|\s)java\b(\S|)(?!\S)/gmi, replacement: "$1Java$2", reason: "Java should be capitalized" }, sql: { expr: /(^|\s)[Ss]ql\b(\S|)(?!\S)/gm, replacement: "$1SQL$2", reason: "SQL is the proper capitalization" }, sqlite: { expr: /(^|\s)[Ss]qlite([0-9]*)\b(\S|)(?!\S)/gm, replacement: "$1SQLite$2$3", reason: "SQLite is the proper capitalization" }, android: { expr: /(^|\s)android\b(\S|)(?!\S)/gmi, replacement: "$1Android$2", reason: "Android should be capitalized" }, oracle: { expr: /(^|\s)oracle\b(\S|)(?!\S)/gmi, replacement: "$1Oracle$2", reason: "Oracle should be capitalized" }, windows: { expr: /(win|windows(?:\ ?)(\s[0-9]+))\b(\S|)(?!\S)/igm, replacement: "Windows$2$3", reason: "Windows should be capitalized" }, windowsXP: { expr: /(win|windows(?:\ ?)(\sxp))\b(\S|)(?!\S)/igm, replacement: "Windows XP$3", reason: "Windows XP should be capitalized" }, windowsVista: { expr: /(win|windows(?:\ ?)(\svista))\b(\S|)(?!\S)/igm, replacement: "Windows Vista$3", reason: "Windows Vista should be capitalized" }, ubuntu: { expr: /(ubunto|ubunut|ubunutu|ubunu|ubntu|ubutnu|ubanto[o]+|unbuntu|ubunt|ubutu)\b(\S|)(?!\S)/igm, replacement: "Ubuntu$2", reason: "corrected Ubuntu spelling" }, linux: { expr: /(linux)\b(\S|)(?!\S)/igm, replacement: "Linux$2", reason: "Linux should be capitalized" }, apostrophes: { expr: /(^|\s)(can|doesn|don|won|hasn|isn|didn)t(\s|$)/gmi, replacement: "$1$2't$3", reason: "English contractions use apostrophes" }, ios: { expr: /\b(?:ios|iOs|ioS|IOS|Ios|IoS|ioS)\b(\S|)(?!\S)/gm, replacement: "iOS$1", reason: "the proper usage is 'iOS'" }, iosnum: { expr: /\b(?:ios|iOs|ioS|IOS|Ios|IoS|ioS)([0-9]?)\b(\S|)(?!\S)/gm, replacement: "iOS $1$2", reason: "the proper usage is 'iOS' followed by a space and the version number" }, yell: { expr: /^((?=.*[A-Z])[^a-z]*)$/g, replacement: "$1", reason: "no need to yell" }, wordpress: { expr: /[Ww]ordpress/g, replacement: "WordPress", reason: "'WordPress' is the proper capitalization" }, google: { expr: /(google)\b(\S|)(?!\S)/igm, replacement: "Google$2", reason: "Google is the proper capitalization" }, mysql: { expr: /(mysql)\b(\S|)(?!\S)/igm, replacement: "MySQL$2", reason: "MySQL is the proper capitalization" }, apache: { expr: /(apache)\b(\S|)(?!\S)/igm, replacement: "Apache$2", reason: "Apache is the proper capitalization" }, git: { expr: /(^|\s)(git|GIT)\b(\S|)(?!\S)/gm, replacement: "$1Git$3", reason: "Git is the proper capitalization" }, harddisk: { expr: /(hdd|harddisk)\b(\S|)(?!\S)/igm, replacement: "hard disk$2", reason: "Hard disk is the proper capitalization" }, github: { expr: /\b([gG]ithub|GITHUB)\b(\S|)(?!\S)/gm, replacement: "GitHub$2", reason: "GitHub is the proper capitalization" }, facebook: { expr: /\b([fF]acebook|FACEBOOK)\b(\S|)(?!\S)/gm, replacement: "Facebook$2", reason: "Facebook is the proper capitalization" }, python: { expr: /(^|\s)[Pp]ython(.|$)/gm, replacement: "$1Python$2", reason: "'Python' is the proper capitalization" }, im: { expr: /(^|\s|\()im(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1I'm$2", reason: "in English, the personal pronoun is 'I'" }, ive: { expr: /(^|\s|\()ive(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1I've$2", reason: "in English, the personal pronoun is 'I'" }, ur: { expr: /(^|\s|\()ur(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1you are$2", reason: "de-text" }, u: { expr: /(^|\s|\()u(\s|,|\.|!|\?|;|\/|\)|'|$)/gm, replacement: "$1you$2", reason: "de-text" }, allways: { expr: /(^|\s|\()(a)llways(\s|,|\.|!|\?|;|\/|\)|'|$)/gmi, replacement: "$1$2lways$3", reason: "spelling" }, appreciated: { expr: /(?:[\s-,']\w*)*(help|suggestion|advice).*(?:appreciated).*/gmi, replacement: "", reason: "$1 requests are unnecessary noise" }, hopeMaybeHelps: { expr: /(?:[\s-,']\w*)*(maybe|hope)+(?:[\s-,']\w*)*\s(help[s]*)(?:[\s-,']\w*)*[\.!?]/gmi, replacement: "", reason: "$1...$2 is unnecessary noise" }, regex: { expr: /regex(p)?/gmi, replacement: function(match,p){ return "RegEx"+((p === undefined)?"":p).toLowerCase(); }, reason: "RegEx or RegExp are the correct capitalizations" }, multiplesymbols: { expr: /\?\?+/gm, replacement: "?", reason: "One question mark for one question" }, // Whitespace compression comes last multiplespaces: { expr: /(\S) +(\S)/gm, replacement: "$1 $2", reason: "One space at a time" }, spacesbeforepunctuation: { //expr: / +([.,:;?!])/g, expr: / +([.,:;?!])[^\w]/g, replacement: "$1 ", reason: "punctuation & spacing" }, spacesafterpunctuation: { expr: /([.,:;?!]) +/g, replacement: "$1 ", reason: "punctuation & spacing" }, leadingspace: { expr: /^ +(\S)/gm, replacement: "$1", reason: "punctuation & spacing" }, blanklines: { expr: /(?:\s*[\r\n]){3,}/gm, replacement: "\n\n", reason: "punctuation & spacing" }, endblanklines: { expr: /[\s\r\n]+$/g, replacement: "", reason: "punctuation & spacing" } }; // Populate funcs App.popFuncs = function() { // This is where the magic happens: this function takes a few pieces of information and applies edits to the post with a couple exceptions App.funcs.fixIt = function(input, expression, replacement, reasoning) { // 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 match = input.search(expression); // If so, increase the number of edits performed (used later for edit summary formation) if (match !== -1) { // Later, this will store what is removed for the first case var phrase; // Then, perform the edits using replace() // What follows is a series of exceptions, which I will explain below; I perform special actions by overriding replace() // This is used for removing things entirely without giving a replacement; it matches the expression and then replaces it with nothing if (replacement === "") { var phrase2; // Hack on a hack - allow 2 replacements input = input.replace(expression, function(data, match1, match2) { // Save what is removed for the edit summary (see below) phrase = match1; phrase2 = match2; // Replace with nothing return ""; }); // This is an interesting tidbit: if you want to make the edit summaries dynamic, you can keep track of a match that you receive // from overriding the replace() function and then use that in the summary reasoning = reasoning.replace("$1", phrase) .replace("$2", phrase2); // This allows me to combine the upvote and downvote replacement schemes into one } else if (replacement == "$1vote") { input = input.replace(expression, function(data, match1) { phrase = match1; return phrase + "vot"; }); reasoning = reasoning.replace("$1", phrase.toLowerCase()); // Fix all caps } else if (reasoning === "no need to yell") { input = input.replace(expression, function(data, match1) { return match1.substring(0, 1).toUpperCase() + match1.substring(1).toLowerCase(); }); // This is used to capitalize letters; it merely takes what is matched, uppercases it, and replaces what was matched with the uppercased version } else if (replacement === "$1") { input = input.replace(expression, function(data, match1) { return match1.toUpperCase(); }); // I can use C, C#, and C++ capitalization in one rule } else if (replacement === "$1C$2") { var newPhrase; input = input.replace(expression, function(data, match1, match2) { newPhrase = match2; return match1 + "C" + match2; }); reasoning = reasoning.replace("$2", newPhrase); // iOS numbering/spacing fixes } else if (replacement === "iOS $2") { input = input.replace(expression, function(data, match1) { if (match1.match(/\d/)) { // Is a number return "iOS " + match1; } return "iOS" + match1; }); // Default: just replace it with the indicated replacement } else { input = input.replace(expression, replacement); } // Return a dictionary with the reasoning for the fix and what is edited (used later to prevent duplicates in the edit summary) return { reason: reasoning, fixed: input }; } else { // If nothing needs to be fixed, return null return null; } }; // Omit code App.funcs.omitCode = function(str, type) { str = str.replace(App.globals.checks[type], function(match) { App.globals.replacedStrings[type].push(match); return App.globals.placeHolders[type]; }); return str; }; // Replace code App.funcs.replaceCode = function(str, type) { for (var i = 0; i < App.globals.replacedStrings[type].length; i++) { str = str.replace(App.globals.placeHolders[type], App.globals.replacedStrings[type][i]); } return str; }; // Eliminate duplicates in array (awesome method I found on SO, check it out!) // From AstroCB: the original structure of the edit formation prevents duplicates. // Unless you changed that structure somehow, this shouldn't be needed. App.funcs.eliminateDuplicates = function(arr) { var i, len = arr.length, out = [], obj = {}; for (i = 0; i < len; i++) { obj[arr[i]] = 0; } for (i in obj) { if (obj.hasOwnProperty(i)) { // Prevents messiness of for..in statements out.push(i); } } return out; }; App.funcs.applyListeners = function() { // Removes default Stack Exchange listeners; see https://github.com/AstroCB/Stack-Exchange-Editor-Toolkit/issues/43 function removeEventListeners(e) { if (e.which === 13) { if (e.metaKey || e.ctrlKey) { // CTRL/CMD + Enter -> Activate the auto-editor App.selections.buttonFix.click(); this.focus(); } else { // It's impossible to remove the event listeners, so we have to clone the element without any listeners var elClone = this.cloneNode(true); this.parentNode.replaceChild(elClone, this); App.selections.submitButton.click(); } } } // Tags box App.selections.tagField.keydown(removeEventListeners); // Edit summary box App.selections.summaryBox.keydown(removeEventListeners); }; // Wait for relevant dynamic content to finish loading App.funcs.dynamicDelay = function(callback) { if (App.globals.inline) { // Inline editing setTimeout(function() { App.selections.buttonBar = $('#wmd-button-bar-' + App.globals.targetID); App.selections.buttonBar.unbind(); setTimeout(function() { callback(); }, 0); }, 500); } else { // Question page editing App.selections.buttonBar = $('#wmd-button-bar-' + App.globals.targetID); // When button bar updates, dynamic DOM is ready for selection App.selections.buttonBar.unbind().on('DOMSubtreeModified', function() { // Avoid running it more than once if (!App.globals.barReady) { App.globals.barReady = true; // Run asynchronously - this lets the bar finish updating before continuing setTimeout(function() { callback(); }, 0); } }); } }; // Populate or refresh DOM selections App.funcs.popSelections = function() { var targetID = App.globals.targetID; var scope = $('div[data-questionid="' + targetID + '"]'); if (!scope.length) scope = $('div[data-answerid="' + targetID + '"]'); if (!scope.length) scope = ''; App.selections.redoButton = $('#wmd-redo-button-' + targetID, scope); App.selections.bodyBox = $("#wmd-input-" + targetID, scope); App.selections.titleBox = $('[class*="title-field"]', scope); App.selections.summaryBox = $("#edit-comment-" + targetID, scope); App.selections.tagField = $($(".tag-editor")[0], scope); App.selections.submitButton = $("#submit-button-" + targetID, scope); }; // Populate edit item sets from DOM selections App.funcs.popItems = function() { App.items[0] = { title: App.selections.titleBox.val().trim(), body: App.selections.bodyBox.val().trim(), summary: App.selections.summaryBox.val().trim() }; }; // Insert editing button(s) App.funcs.createButton = function() { // Insert button App.selections.redoButton.after(App.selections.buttonWrapper); // Insert spacer App.selections.redoButton.after(App.globals.spacerHTML); }; // Style button App.funcs.styleButton = function() { var buttonCSS = { 'position': 'relative', 'left': '430px', 'padding-top': '2%' }; $("#wmd-help-button-" + App.globals.targetID).css({ 'padding': '0px' }); App.selections.buttonWrapper.css(buttonCSS); App.selections.buttonFix.css({ 'position': 'static', 'float': 'left', 'border-width': '0px', 'background-color': 'white', 'background-image': 'url("//i.imgur.com/79qYzkQ.png")', 'background-size': '100% 100%', 'width': '18px', 'height': '18px', 'outline': 'none', 'box-shadow': 'none' }); App.selections.buttonInfo.css({ 'position': 'static', 'float': 'left', 'margin-left': '5px', 'font-size': '12px', 'color': '#424242', 'line-height': '19px' }); App.selections.buttonFix.hover(function() { App.globals.infoContent = App.selections.buttonInfo.text(); App.selections.buttonInfo.text('Fix the content!'); App.selections.buttonFix.css({ 'background-image': 'url("//i.imgur.com/d5ZL09o.png")' }); }, function() { App.selections.buttonInfo.text(App.globals.infoContent); App.selections.buttonFix.css({ 'background-image': 'url("//i.imgur.com/79qYzkQ.png")' }); }); }; // Listen to button click App.funcs.listenButton = function() { App.selections.buttonFix.click(function(e) { e.preventDefault(); // Refresh item population App.funcs.popItems(); // Pipe data through editing modules App.pipe(App.items, App.globals.pipeMods, App.globals.order); App.globals.editsMade = true; }); }; // Figure out the last selected element before pressing the button so we can return there after focusing the summary field App.funcs.setLastFocus = function() { App.selections.titleBox.click(function() { App.globals.lastSelectedElement = $(this); }); App.selections.bodyBox.click(function() { App.globals.lastSelectedElement = $(this); }); App.selections.summaryBox.click(function() { App.globals.lastSelectedElement = $(this); }); App.selections.tagField.click(function() { App.globals.lastSelectedElement = $(this); }); }; // Handle pipe output App.funcs.output = function(data) { App.selections.titleBox.val(data[0].title); App.selections.bodyBox.val(data[0].body); App.selections.summaryBox.val(data[0].summary); // Update the comment: focusing on the input field to remove placeholder text, but scroll back to the user's original location App.globals.currentPos = document.body.scrollTop; if ($("#wmd-input")) { $("#wmd-input").focus(); $("#edit-comment").focus(); $("#wmd-input").focus(); } else { $(".wmd-input")[0].focus(); $(".edit-comment")[0].focus(); $(".wmd-input")[0].focus(); } window.scrollTo(0, App.globals.currentPos); App.globals.infoContent = App.globals.reasons.length + ' changes made'; App.selections.buttonInfo.text(App.globals.infoContent); }; }; // 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]; data = mods[modName](data); } } App.funcs.output(data); }; // Init app App.init = function() { App.popFuncs(); App.funcs.dynamicDelay(function() { App.funcs.popSelections(); App.funcs.createButton(); App.funcs.styleButton(); App.funcs.popItems(); App.funcs.listenButton(); App.funcs.applyListeners(); App.funcs.setLastFocus(); }); }; App.globals.pipeMods.omit = function(data) { data[0].body = App.funcs.omitCode(data[0].body, "block"); data[0].body = App.funcs.omitCode(data[0].body, "inline"); return data; }; App.globals.pipeMods.replace = function(data) { data[0].body = App.funcs.replaceCode(data[0].body, "block"); data[0].body = App.funcs.replaceCode(data[0].body, "inline"); return data; }; App.globals.pipeMods.edit = function(data) { // Visually confirm edit - SE makes it easy because the jQuery color animation plugin seems to be there by default App.selections.bodyBox.animate({ backgroundColor: '#c8ffa7' }, 10); App.selections.bodyBox.animate({ backgroundColor: '#fff' }, 1000); // Loop through all editing rules for (var j in App.edits) { if (App.edits.hasOwnProperty(j)) { // Check body var fix = App.funcs.fixIt(data[0].body, App.edits[j].expr, App.edits[j].replacement, App.edits[j].reason); if (fix) { App.globals.reasons[App.globals.reasons.length] = fix.reason; data[0].body = fix.fixed; App.edits[j].fixed = true; } // Check title fix = App.funcs.fixIt(data[0].title, App.edits[j].expr, App.edits[j].replacement, App.edits[j].reason); if (fix) { data[0].title = fix.fixed; if (!App.edits[j].fixed) { App.globals.reasons[App.globals.reasons.length] = fix.reason; App.edits[j].fixed = true; } } } } // If there are no reasons, exit if(!App.globals.reasons.length) return false; // Eliminate duplicate reasons App.globals.reasons = App.funcs.eliminateDuplicates(App.globals.reasons); var tmpSummary = ''; for (var z = App.globals.reasons.length - 1, x = 0; z >= 0; --z) { // Check that summary is not getting too long if (data[0].summary.length + tmpSummary.length + App.globals.reasons[z].length + 2 > 300) break; // If the reason already exists, skip it if (data[0].summary.indexOf(App.globals.reasons[z]) !== -1) continue; // Capitalize first letter if (x === 0) App.globals.reasons[z] = App.globals.reasons[z][0].toUpperCase() + App.globals.reasons[z].substring(1); // If the reason already exists, skip it if (data[0].summary.indexOf(App.globals.reasons[z]) !== -1) continue; // Append the reason and a semicolon (or period if it is the last reason) to the summary tmpSummary += App.globals.reasons[z] + (z === 0 ? "." : "; "); ++x; } // If no reasons have been applied, exit. if (!tmpSummary) return false; // Store the summary for readability var summary = data[0].summary; // This whole ternary mess is for if the summary is not empty, and if this is the first time around or not data[0].summary = (summary ? (summary.substr(-1) !== -1 ? summary.substr(0,summary.length-1) : summary) + '; ' : '') + tmpSummary; // Focus the summary field. App.selections.summaryBox.focus(); return data; }; App.init(inline, targetID); } var Apps = []; var selector = '.edit-post, [value*="Edit"]:not([value="Save Edits"])'; var clickables = $(selector); if (clickables.length) { clickables.click(function(e) { try { Apps.push(new EditorToolkit(true, e.target.href ? e.target.href.match(/\d/g).join("") : $('.post-id').text())); } catch (e) { console.log(e); } }); } else { Apps.push(EditorToolkit(false)); } }; // Inject the main script var script = document.createElement('script'); script.type = "text/javascript"; script.textContent = '(' + main.toString() + ')();'; document.body.appendChild(script);
查找差异