Diff
checker
テキスト
テキスト
画像
ドキュメント
Excel
フォルダ
Legal
Enterprise
デスクトップ
料金
ログイン
Diffchecker デスクトップのダウンロード
テキスト比較
2 つのテキスト ファイルの違いを見つける
ツール
履歴
ライブエディター
未変更行を折りたたむ
折り返しなし
レイアウト
分割
統合
比較精度
スマート
単語
文字
シンタックスハイライト
構文を選択
無視
テキスト変換
最初の差分へ移動
入力を編集
Diffchecker Desktop
Diffcheckerを実行する最も安全な方法。Diffchecker Desktopアプリを入手:あなたの差分はコンピューターから出ることはありません!
Desktopを入手
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);
違いを見つける