Diff
checker
Texto
Texto
Imagens
Documentos
Excel
Pastas
Legal
Enterprise
Aplicativo para desktop
Preços
Fazer login
Baixar o Diffchecker Desktop
Comparar texto
Encontre a diferença entre dois arquivos de texto
Ferramentas
Histórico
Editor live
Ocultar espaços em branco
Recolher inalteradas
Sem quebra de linha
Layout
Dividido
Unificado
Nível de detalhe
Inteligente
Palavra
Caractere
Estilos de texto
Alterar aparência
Realce de sintaxe
Escolher sintaxe
Ignorar
Transformar texto
Ir à primeira mudança
Editar entrada
Diffchecker Desktop
A maneira mais segura de usar o Diffchecker. Obtenha o aplicativo Diffchecker Desktop: seus diffs nunca saem do seu computador!
Obter Desktop
Untitled diff
Criado
há 11 anos
O diff nunca expira
Limpar
Exportar
Compartilhar
Explicar
515 remoções
Linhas
Total
Removido
Caracteres
Total
Removido
Para continuar usando este recurso, atualize para
Diff
checker
Pro
Ver preços
581 linhas
Copiar tudo
526 adições
Linhas
Total
Adicionado
Caracteres
Total
Adicionado
Para continuar usando este recurso, atualize para
Diff
checker
Pro
Ver preços
633 linhas
Copiar tudo
// ==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
Copiar
Copiado
Copiar
Copiado
// @contributor Tiny Giant
// @contributor Mogsdad
// @grant none
// @license MIT
// @license MIT
// @namespace http://github.com/AstroCB
// @namespace http://github.com/AstroCB
Copiar
Copiado
Copiar
Copiado
// @version 1.5.
1
// @version 1.5.
2.25
// @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
Copiar
Copiado
Copiar
Copiado
// @include
*://*.stackexchange.com/questions/*
// @include
/^https?://\w*.?(stackoverflow|stackexchange|serverfault|superuser|askubuntu|stackapps)\.com/(questions|posts|review)/(?!tagged|new).*/
// @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/*
// @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==
// ==/UserScript==
Copiar
Copiado
Copiar
Copiado
(function()
{
(function()
{
"use strict";
"use strict";
Copiar
Copiado
Copiar
Copiado
var main =
function
(
) {
function
extendEditor(root
) {
// Define app namespace
var App = {};
var App = {};
// Place edit items here
// Place edit items here
Copiar
Copiado
Copiar
Copiado
App.items =
[]
;
App.items =
{};
App.originals = {}
;
// 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 = {};
Copiar
Copiado
Copiar
Copiado
// True to display rule names in Edit Summary
App.globals.showRules = false;
//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';
Copiar
Copiado
Copiar
Copiado
// Populate global data
App.globals.root = root;
// Get url for question id used in id and class names
App.globals.URL = window.location.href;
// Get question num from URL
App.globals.questionNum = App.globals.URL.match(/\/(\d+)\//g);
if (App.globals.questionNum) {
App.globals.questionNum = App.globals.questionNum[0].split("/").join("");
}
// Define variables for later use
App.globals.barReady = false;
App.globals.editsMade = false;
App.globals.editCount = 0;
App.globals.infoContent = '';
Copiar
Copiado
Copiar
Copiado
App.globals.spacerHTML = '<li class="wmd-spacer wmd-spacer3" id="wmd-spacer3
-' + App.globals.questionNum + '
" style="left: 400px !important;"></li>';
App.globals.spacerHTML = '<li class="wmd-spacer wmd-spacer3" id="wmd-spacer3
" style="left: 400px !important;"></li>';
App.globals.buttonHTML = '<div id="ToolkitButtonWrapper"><button class="wmd-button" id="ToolkitFix"></button><div id="ToolkitInfo"></div></div>';
Copiar
Copiado
Copiar
Copiado
App.globals.reasons =
[];
App.globals.reasons =
{}
;
App.globals.numReasons = 0
;
App.globals.replacedStrings = {
App.globals.replacedStrings = {
Copiar
Copiado
Copiar
Copiado
"
block
":
[],
"
auto
":
[],
"inline": []
"quote":
[],
"inline": []
,
"block": [],
"links": [],
"tags": []
};
};
App.globals.placeHolders = {
App.globals.placeHolders = {
Copiar
Copiado
Copiar
Copiado
"
block": "_xCodexBlockxPlacexHolderx_",
"
auto": "_xAutoxInsertxTextxPlacexHolder_",
"inline": "_xCodexInlinexPlacexHolderx_"
"quote": "_xBlockxQuotexPlacexHolderx_",
"inline": "_xCodexInlinexPlacexHolderx_"
,
"block": "_xCodexBlockxPlacexHolderx_",
"links": "_xLinkxPlacexHolderx_",
"tags": "_xTagxPlacexHolderx_"
};
App.globals.placeHolderChecks = {
"auto": /_xAutoxInsertxTextxPlacexHolder_/gi,
"quote": /_xBlockxQuotexPlacexHolderx_/gi,
"inline": /_xCodexInlinexPlacexHolderx_/gi,
"block": /_xCodexBlockxPlacexHolderx_/gi,
"links": /_xLinkxPlacexHolderx_/gi,
"tags": /_xTagxPlacexHolderx_/gi
};
};
App.globals.checks = {
App.globals.checks = {
Copiar
Copiado
Copiar
Copiado
"block": /( )+.*/gm
,
// https://regex101.com/r/cI6oK2/1 automatically inserted text
"inline": /`.*`/gm
"auto": /[^]*\<\!\-\- End of automatically inserted text \-\-\>/g,
// https://regex101.com/r/fU5lE6/1 blockquotes
"quote": /^\>(?:(?!\n\n)[^])+/gm,
// https://regex101.com/r/lL6fH3/1 single-line inline code
"inline": /`[^`\n]+`/g,
// https://regex101.com/r/eC7mF7/1 code blocks and multiline inline code.
"block": /`[^`]+`|(?:(?:[ ]{4}|[ ]{0,3}\t).+(?:[\r\n]?(?!\n\S)(?:[ ]+\n)*)+)+/g,
// https://regex101.com/r/tZ4eY3/5 links and link-sections
"links": /\[[^\]\n]+\](?:\([^\)\n]+\)|\[[^\]\n]+\])|(?: (?:\[\d\]): \w*:+\/\/.*\n*)+|(?!.net)(?:\/\w+|.:\\|\.[^ \n\r.]+|\w+:\/\/)[^\s)]*/g
,
// tags and html comments TODO: needs test
"tags": /\<[\/a-z]+\>|\<\!\-\-[^>]+\-\-\>/g
};
};
// 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
Copiar
Copiado
Copiar
Copiado
App.globals.order = ["omit"
, "edit", "replace"];
App.globals.order = ["omit"
, "codefix"
, "edit", "replace"];
// Define edit rules
// Define edit rules
App.edits = {
App.edits = {
Copiar
Copiado
Copiar
Copiado
i
: {
// All caps
expr: /
(^|\s|\()i(\s|,|\.|!|\?|;|\/|\)|'|$)/gm
,
noneedtoyell
: {
replacement:
"$1I$2",
expr: /
^((?=.*[A-Z])[^a-z]*)$/g
,
reason:
"in English, the pronoun 'I' is capitalized"
replacement:
function(input) {
return input.trim().substr(0, 1).toUpperCase() + input.trim().substr(1).toLowerCase();
},
reason:
'no need to yell'
},
},
Copiar
Copiado
Copiar
Copiado
// Trademark capitalization
so: {
so: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)[Ss]
tack\s*overflow
|StackOverflow(.|$)/gm
,
expr: /
\bs
tack\s*overflow
\b/gi
,
replacement: "
$1
Stack Overflow
$2
",
replacement: "
Stack Overflow
",
reason: "'Stack Overflow' is the legal name"
reason: "'Stack Overflow' is the legal name"
},
},
se: {
se: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)[Ss]
tack\s*exchange
|StackExchange(.|$)/gm
,
expr: /
\bs
tack\s*exchange
\b/gi
,
replacement: "
$1
Stack Exchange
$2
",
replacement: "
Stack Exchange
",
reason: "'Stack Exchange' is the legal name"
reason: "'Stack Exchange' is the legal name"
},
},
expansionSO: {
expansionSO: {
Copiar
Copiado
Copiar
Copiado
expr: /(
^|\s
)SO
(\s|,|\.|!|\?|;|\/|\)|$)/gm
,
expr: /(
[^\b\w.]|^
)SO
\b/g
,
replacement: "$1Stack Overflow
$2
",
replacement: "$1Stack Overflow
",
reason: "'
SO' expansion
"
reason: "'
Stack Overflow' is the legal name
"
},
},
expansionSE: {
expansionSE: {
Copiar
Copiado
Copiar
Copiado
expr: /(
^|\s
)SE
(\s|,|\.|!|\?|;|\/|\)|$)/gm
,
expr: /(
[^\b\w.]|^
)SE
\b/g
,
replacement: "$1Stack Exchange
$2
",
replacement: "$1Stack Exchange
",
reason: "'
SE' expansion
"
reason: "'
Stack Exchange' is the legal name
"
},
},
javascript: {
javascript: {
Copiar
Copiado
Copiar
Copiado
expr: /(
^|\s)[Jj]
ava
\s*[Ss]
cript
(.|$)/gm
,
expr: /(
[^\b\w.]|^)(j
ava
s
cript
|js)\b/gi
,
replacement: "$1JavaScript
$2
",
replacement: "$1JavaScript
",
reason: "
'JavaScript' is the proper
capitalization"
reason: "
trademark
capitalization"
},
},
jsfiddle: {
jsfiddle: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)[Jj][Ss]\s*[Ff]iddle(.|$)/gm
,
expr: /
\bjsfiddle\b/gi
,
replacement: "
$1
JSFiddle
$2
",
replacement: "
JSFiddle
",
reason: "
'JSFiddle' is the currently accepted
capitalization"
reason: "
trademark
capitalization"
},
caps: {
expr: /^(?!https?)([a-z])/gm,
replacement: "$1",
reason: "copy edited"
},
},
jquery: {
jquery: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)[Jj][Qq]
uery
(.|$)/gm
,
expr: /
\bjq
uery
\b/gi
,
replacement: "
$1
jQuery
$2
",
replacement: "
jQuery
",
reason: "
'jQuery' is the proper
capitalization"
reason: "
trademark capitalization"
},
angular: {
expr: /\bangular(?:js)?\b/gi,
replacement: "AngularJS",
reason: "trademark
capitalization"
},
},
html: {
html: {
Copiar
Copiado
Copiar
Copiado
expr: /(
^|\s)[Hh]
tml(
[5]?)\b(\S|)(?!\S)/gm
,
expr: /(
[^\b\w.]|^)h
tml(
\d)?\b/gi
,
replacement: "$1HTML$2
$3
",
replacement: "$1HTML$2
",
reason: "
HTML stands for HyperText Markup Language
"
reason: "
trademark capitalization
"
},
},
css: {
css: {
Copiar
Copiado
Copiar
Copiado
expr: /(
^|\s)[Cc]
ss\b
(\S|)(?!\S)/gm
,
expr: /(
[^\b\w.]|^)c
ss\b
/gi
,
replacement: "$1CSS
$2
",
replacement: "$1CSS
",
reason: "
CSS stands for Cascading Style Sheets
"
reason: "
trademark capitalization
"
},
},
json: {
json: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)[Jj]
son\b
(\S|)(?!\S)/gm
,
expr: /
\bj
son\b
/gi
,
replacement: "
$1
JSON
$2
",
replacement: "
JSON
",
reason: "
JSON stands for JavaScript Object Notation
"
reason: "
acronym capitalization
"
},
},
ajax: {
ajax: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)
ajax\b
(\S|)(?!\S)/gm,
expr: /
\b
ajax\b
/gi,
replacement: "$1AJAX$2",
replacement: "
AJAX
",
reason: "AJAX stands for Asynchronous JavaScript and XML"
reason: "
acronym
capitalization"
},
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: {
php: {
Copiar
Copiado
Copiar
Copiado
expr: /(
^|\s)[Pp]
hp\b
(\S|)(?!\S)/gm
,
expr: /(
[^\b\w.]|^)p
hp\b
/gi
,
replacement: "$1PHP
$2
",
replacement: "$1PHP
",
reason: "
PHP stands for PHP: Hypertext Preprocessor"
reason: "
trademark capitalization"
},
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: {
voting: {
Copiar
Copiado
Copiar
Copiado
expr: /
([Dd]
own|
[Uu]p)[\s*\-]
vot/g
,
expr: /
\b(d
own|
up)\W
vot/g
i
,
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)"
},
},
Copiar
Copiado
Copiar
Copiado
mysite: {
expr: /mysite\./g,
replacement: "example.",
reason: "links to mysite.domain are not allowed: use example.domain instead"
},
c: {
c: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)c(#|\++|\s|$)/gm
,
expr: /
\bc\b([#+]+)?/gi
,
replacement: "
$1C$2
",
replacement: "
C$1
",
reason: "
C$2 is the proper
capitalization"
reason: "
trademark
capitalization"
},
},
java: {
java: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)
java\b
(\S|)(?!\S)/gm
i,
expr: /
\b
java\b
/g
i,
replacement: "
$1
Java
$2
",
replacement: "
Java
",
reason: "
Java should be
capitaliz
ed
"
reason: "
trademark
capitaliz
ation
"
},
},
sql: {
sql: {
Copiar
Copiado
Copiar
Copiado
expr: /(
^|\s)[Ss]
ql\b
(\S|)(?!\S)/gm
,
expr: /(
[^\b\w.]|^)s
ql\b
/gi
,
replacement: "$1SQL
$2
",
replacement: "$1SQL
",
reason: "
SQL is the proper
capitalization"
reason: "
trademark
capitalization"
},
},
sqlite: {
sqlite: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)[Ss]
qlite
([0-9]*)\b
(\S|)(?!\S)/gm
,
expr: /
\bs
qlite
\s*
([0-9]*)\b
/gi
,
replacement: "
$1
SQLite
$2$3
",
replacement: "
SQLite
$2
",
reason: "
SQLite is the proper
capitalization"
reason: "
trademark
capitalization"
},
},
android: {
android: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)
android\b
(\S|)(?!\S)/gm
i,
expr: /
\b
android\b
/g
i,
replacement: "
$1
Android
$2
",
replacement: "
Android
",
reason: "
Android should be
capitaliz
ed
"
reason: "
trademark
capitaliz
ation
"
},
},
oracle: {
oracle: {
Copiar
Copiado
Copiar
Copiado
expr: /
(^|\s)
oracle\b
(\S|)(?!\S)/gm
i,
expr: /
\b
oracle\b
/g
i,
replacement: "
$1
Oracle
$2
",
replacement: "
Oracle
",
reason: "
Oracle should be
capitaliz
ed
"
reason: "
trademark
capitaliz
ation
"
},
},
windows: {
windows: {
Copiar
Copiado
Copiar
Copiado
expr: /
(
win|windows
(?:\ ?)(\s
[0-9
]+)
)\b
(\S|)(?!\S)/igm
,
// https://regex101.com/r/jF9zK1/5
replacement:
"Windows$2$3",
expr: /
\b(?:
win|windows
)\s+(2k|
[0-9
.]+|ce|me|nt|xp|vista|server)|(?:win|windows
)\b
/gi
,
reason: "Windows should be capitalized"
replacement:
function(match, ver) {
},
ver = !ver ? '' : ver.replace(/ce/i, ' CE')
windowsXP: {
.replace(/me/i, ' ME')
expr: /(win|windows(?:\ ?)(\sxp))\b(\S|)(?!\S)/igm,
.
replace
(/nt/i, ' NT')
replace
ment: "Windows XP$3",
.replace(/xp/i, ' XP')
reason: "Windows XP should be capitalized"
.replace(/2k/i, ' 2000')
},
.
replace
(/vista/i, '
Vista
')
windowsVista: {
.replace(/server/i, ' Server');
expr: /(win|windows(?:\ ?)(\svista))\b(\S|)(?!\S)/igm,
return 'Windows' + ver;
replace
ment: "Windows
Vista
$3",
}
,
reason: "Windows Vista should be capitalized"
reason: "
trademark capitalization
"
},
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: {
linux: {
Copiar
Copiado
Copiar
Copiado
expr: /
(
linux
)\b(\S|)(?!\S)/igm
,
expr: /
\b
linux
\b/gi
,
replacement: "Linux
$2
",
replacement: "Linux
",
reason: "
Linux should be capitalized"
reason: "
trademark capitalization"
},
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"
},
caps: {
expr: /^((?=.*[A-Z])[^a-z]*)$/g,
replacement: "$1",
reason: "no need to yell"
},
},
wordpress: {
wordpress: {
Copiar
Copiado
Copiar
Copiado
expr: /
[Ww]
ordpress
/g
,
expr: /
\bw
ordpress
\b/gi
,
replacement: "WordPress",
replacement: "WordPress",
Copiar
Copiado
Copiar
Copiado
reason: "
'WordPress' is the proper
capitalization"
reason: "
trademark
capitalization"
},
},
google: {
google: {
Copiar
Copiado
Copiar
Copiado
expr:
/(
google
)\b(\S|)(?!\S)/igm
,
expr:
/\b
google
\b/gi
,
replacement: "Google
$2
",
replacement: "Google
",
reason: "
Google is the proper
capitalization"
reason: "
trademark
capitalization"
},
},
mysql: {
mysql: {
Copiar
Copiado
Copiar
Copiado
expr:
/(
mysql
)\b(\S|)(?!\S)/igm
,
expr:
/\b
mysql
\b/gi
,
replacement: "MySQL
$2
",
replacement: "MySQL
",
reason: "
MySQL is the proper
capitalization"
reason: "
trademark
capitalization"
},
},
apache: {
apache: {
Copiar
Copiado
Copiar
Copiado
expr:
/(
apache
)\b(\S|)(?!\S)/igm
,
expr:
/\b
apache
\b/gi
,
replacement: "Apache
$2
",
replacement: "Apache
",
reason: "
Apache is the proper
capitalization"
reason: "
trademark
capitalization"
},
},
git: {
git: {
Copiar
Copiado
Copiar
Copiado
expr:
/(^|\s)(
git
|GIT)\b(\S|)(?!\S)/gm
,
expr:
/\b
git
\b/gi
,
replacement: "
$1
Git
$3
",
replacement: "
Git
",
reason: "
Git is the proper
capitalization"
reason: "
trademark
capitalization"
},
harddisk: {
expr: /(hdd|harddisk)\b(\S|)(?!\S)/igm,
replacement: "hard disk$2",
reason: "Hard disk is the proper capitalization"
},
},
github: {
github: {
Copiar
Copiado
Copiar
Copiado
expr:
/\b([gG]ithub|GITHUB)\b(\S|)(?!\S)/gm,
expr:
/\bgithub\b/gi,
replacement: "
GitHub
$2",
replacement: "GitHub",
reason: "
GitHub is the proper capitalization"
reason: "trademark capitalization"
},
facebook: {
expr: /\bfacebook\b/gi,
replacement: "Facebook",
reason: "trademark capitalization"
},
python: {
expr: /\bpython\b/gi,
replacement: "Python",
reason: "trademark capitalization"
},
urli: {
expr: /\b(ur[li])\b/gi,
replacement: function(match) {
return match.toUpperCase();
},
reason: "acronym capitalization"
},
ios: {
expr: /\bios\b/gi,
replacement: "iOS",
reason: "trademark capitalization"
},
iosnum: {
expr: /\bios([0-9])\b/gi,
replacement: "iOS $1",
reason: "trademark capitalization"
},
ubunto: {
expr: /\b[uoa]*b[uoa]*[tn][oua]*[tnu][oua]*\b/gi,
replacement: "Ubuntu",
reason: "trademark capitalization"
},
vbnet: {
expr: /(?:vb)?(?:\.net|\s?[0-9]+)\s?(?:framework|core)?/gi,
replacement: function(str) {
return str.replace(/vb/i, 'VB')
.replace(/net/i, 'NET')
.replace(/framework/i, 'Framework')
.replace(/core/i, 'Core');
},
reason: "trademark capitalization"
},
regex: {
expr: /\bregex(p)?/gi,
replacement: "RegEx$1",
reason: "trademark capitalization"
},
// Noise reduction
editupdate: {
// https://regex101.com/r/tT2pK6/2
expr: /(?!(?:edit|update)\w*\s*[^:]*$)(?:^\**)(edit|update)\w*(\s*#?[0-9]+)?:?(?:\**):?/gmi,
replacement: "",
reason: "noise reduction"
},
hello: { // TODO: Update badsentences (new) to catch everything hello (old) did.
expr: /(?:^|\s)(hi\s+guys|hi|hello|good\s(?:evening|morning|day|afternoon))(?:\.|!|\ )/gmi,
replacement: "",
reason: "noise reduction"
},
badwords: {
expr: /[^\n.!?:]*\b(?:th?anks?|th(?:an)?x|tanx|folks?|kind(?:est|ly)|first\s*question)\b[^,.!?\n]*[,.!?]*/gi,
replacement: "",
reason: "noise reduction"
},
badphrases: {
expr: /[^\n.!?:]*(?:h[ea]lp|hope|appreciate|pl(?:ease|z|s))[^.!?\n]*(?:helps?|appreciated?)[^,.!?\n]*[,.!?]*/gi,
replacement: "",
reason: "noise reduction"
},
imnew: {
expr: /(?! )[\w\s]*\bi[' ]?a?m +(?:kinda|really) *new\w* +(?:to|in) *\w* *(?:and|[;,.!?])? */gi,
replacement: "",
reason: "noise reduction"
},
salutations: {
expr: /[\r\n]*(regards|cheers?),?[\t\f ]*[\r\n]?\w*\.?/gi,
replacement: "
",
reason: "noise reduction"
},
// Grammar and spelling
apostrophe_d: {
expr: /\b(he|she|who|you)[^\w']*(d)\b/gi,
replacement: "$1'
$2",
reason: "
grammar and spelling"
},
apostrophe_ll: {
expr: /\b(they|what|who|you)[^\w']*(ll)\b/gi,
replacement: "$1'$2",
reason: "grammar and spelling"
},
apostrophe_re: {
expr: /\b(they|what|you)[^\w']*(re)\b/gi,
replacement: "$1'$2",
reason: "grammar and spelling"
},
apostrophe_s: {
expr: /\b(he|she|that|there|what|where)[^\w']*(s)\b/gi,
replacement: "$1'$2",
reason: "grammar and spelling"
},
apostrophe_t: {
expr: /\b(aren|can|didn|doesn|don|hasn|haven|isn|mightn|mustn|shan|shouldn|won|wouldn)[^\w']*(t)\b/gi,
replacement: "$1'$2",
reason: "grammar and spelling"
},
prolly: {
expr: /\bproll?y\b/gi,
replacement: "probably",
reason: "grammar and spelling"
},
i: {
expr: /\bi('|\b)/g, // i or i-apostrophe
replacement: "I",
reason: "grammar and spelling"
},
im: {
expr: /\bim\b/gi,
replacement: "I'm",
reason: "grammar and spelling"
},
ive: {
expr: /\bive\b/gi,
replacement: "I've",
reason: "grammar and spelling"
},
ur: {
expr: /\bur\b/gi,
replacement: "your", // May also be "you are", but less common on SO
reason: "grammar and spelling"
},
u: {
expr: /\bu\b/gi,
replacement: "you",
reason: "grammar and spelling"
},
gr8: {
expr: /\bgr8\b/gi,
replacement: "great",
reason: "grammar and spelling"
},
allways: {
expr: /\b(a)llways\b/gi,
replacement: "$1lways",
reason: "grammar and spelling"
},
// Punctuation & Spacing come last
firstcaps: {
// https://regex101.com/r/qR5fO9/12
// This doesn't work quite right, because is finds all sentences, not just ones needing caps.
//expr: /(?:(?!\n\n)[^\s.!?]+[ ]*)+([.!?])*[ ]*/g,
expr: /((?!\n\n)(?:[^?.!])*([?.!]|\n\n)?\)*)/gm,
replacement: function(str, endpunc) {
if (str === "undefined") return '';
console.log('str: '+str);
// https://regex101.com/r/bL9xD7/1 find and capitalize first letter
return str.replace(/^(\W*)([a-z])(.*)/g, function(sentence, pre, first, post) {
if (!pre) pre = '';
if (!post) post = '';
console.log('sentence ('+sentence+') pre ('+pre+') first ('+first+') post ('+post+') endpunc ('+endpunc+')');
var update = pre + first.toUpperCase() + post// + (!endpunc && /\w/.test(post.substr(-1)) ? '.' : '');
console.log('update ('+update+')');
return update;
});
},
reason: "Caps at start of sentences"
},
multiplesymbols: {
// https://regex101.com/r/bE9zM6/1
expr: /([^\w\s*#.\-_])\1{1,}/g,
replacement: "$1",
reason: "punctuation & spacing"
},
spacesbeforesymbols: {
expr: /\s+([.,!?;:])(?!\w)/g,
replacement: "$1",
reason: "punctuation & spacing"
},
multiplespaces: {
// https://regex101.com/r/hY9hQ3/1
expr: /[ ]{2,}(?!$)/g,
replacement: " ",
reason: "punctuation & spacing"
}
}
};
};
Copiar
Copiado
Copiar
Copiado
// Populate funcs
// 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.popFuncs = function() {
App.funcs.fixIt = function(input, expression, replacement, reasoning) {
// This is where the magic happens: this function takes a few pieces of information and applies edits to the post with a couple exceptions
// If there is nothing to search, exit
App.funcs.fixIt = function(input, expression, replacement, reasoning) {
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
es
= input.
match
(expression);
// If so, increase the number of edits performed (used later for edit summary formation)
if (!matches) return false;
if (match !== -1) {
console.log(JSON.stringify(matches))
App.globals.editCount++;
var count = matches.length; // # replacements to do
var tmpinput = input;
input = input.replace(expression, function() {
var matches = [].slice.call(arguments, 0, -2);
reasoning = reasoning.replace(/[$](\d)+/g, function() {
var phrases = [].slice.call(arguments, 0, -2);
var phrase = matches[phrases[1]];
return phrase ? phrase : '';
});
return arguments[0].replace(expression, replacement);
});
if (input !== tmpinput) {
return {
reason: reasoning,
fixed: String(input).trim(),
count: count
};
} else return false;
};
Copiar
Copiado
Copiar
Copiado
// Later, this will store what is removed for the first case
App.funcs.applyListeners = function() { // Removes default Stack Exchange listeners; see https://github.com/AstroCB/Stack-Exchange-Editor-Toolkit/issues/43
var phrase;
function removeEventListeners(e) {
if (e.which === 13) {
if (e.metaKey || e.ctrlKey) {
// CTRL/CMD + Enter -> Activate the auto-editor
App.selections.buttonFix.click();
} else {
// It's possible to remove the event listeners, because of the way outerHTML works.
this.outerHTML = this.outerHTML;
App.selections.submitButton.click();
}
}
}
Copiar
Copiado
Copiar
Copiado
//
Store the original input text
//
Tags box
var originalInput = input
;
App.selections.tagField.keydown(removeEventListeners)
;
Copiar
Copiado
Copiar
Copiado
// Then, perform the edits using replace()
// Edit summary box
// What follows is a series of exceptions, which I will explain below; I perform special actions by overriding replace()
App.selections.summary.keydown(removeEventListeners);
// This is used for removing things entirely without giving a replacement; it matches the expression and then replaces it with nothing
};
if (replacement === "") {
input = input.replace(expression, function(data, match1) {
// Save what is removed for the edit summary (see below)
phrase = match1;
Copiar
Copiado
Copiar
Copiado
// Replace with nothing
// Populate or refresh DOM selections
return "";
App.funcs.popSelections = function() {
});
App.selections.redoButton = App.globals.root.find('[id^="wmd-redo-button"]');
App.selections.body = App.globals.root.find('[id^="wmd-input"]');
App.selections.title = App.globals.root.find('[class*="title-field"]');
App.selections.summary = App.globals.root.find('[id^="edit-comment"]');
App.selections.tagField = App.globals.root.find(".tag-editor");
App.selections.submitButton = App.globals.root.find('[id^="submit-button"]');
App.selections.helpButton = App.globals.root.find('[id^="wmd-help-button"]');
App.selections.editor = App.globals.root.find('.post-editor');
};
Copiar
Copiado
Copiar
Copiado
// This is an interesting tidbit: if you want to make the edit summaries dynamic, you can keep track of a match that you receive
// Populate edit item sets from DOM selections
// from overriding the replace() function and then use that in the summary
App.funcs.popItems = function() {
reasoning = reasoning.replace("$1", phrase);
var i = App.items, s = App.selections;
['title', 'body', 'summary'].forEach(function(v) {
i[v] = String(s[v].val()).trim();
});
};
Copiar
Copiado
Copiar
Copiado
// This allows me to combine the upvote and downvote replacement schemes into one
// Populate original item sets from DOM selections
} else if (replacement == "$1vote"
) {
App.funcs.popOriginals = function(
) {
input = input.replace(expression,
function(
data, match1
) {
var i = App.originals, s = App.selections;
phrase = match1;
['title', 'body', 'summary'].forEach(
function(
v
) {
return phrase + "vot";
i[v] = String(s[v].val()).trim();
});
});
reasoning = reasoning.replace("$1", phrase.toLowerCase())
;
}
;
Copiar
Copiado
Copiar
Copiado
// Fix all caps
// Insert editing button(s)
} else if (reasoning === "no need to yell") {
App.funcs.createButton =
function(
) {
input = input.replace(expression,
function(
data, match1
) {
if (!App.selections.redoButton.length) return false;
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();
});
Copiar
Copiado
Copiar
Copiado
// I can use C, C#, and C++ capitalization in one rule
App.selections.buttonWrapper = $('<div class="ToolkitButtonWrapper"/>');
} else if (replacement === "$1C$2") {
App.selections.buttonFix = $('<button class="wmd-button ToolkitFix" title="Fix the content!" />');
var newPhrase;
App.selections.buttonInfo
=
$('<div class="ToolkitInfo">'
);
input = input.replace(expression, function(data, match1, match2) {
newPhrase = match2;
return match1 + "C" + match2;
});
reasoning
=
reasoning.replace("$2", newPhrase
);
Copiar
Copiado
Copiar
Copiado
// iOS numbering/spacing fixes
// Build the button
} else if (replacement === "iOS $2") {
App.selections.buttonWrapper.append(App.selections.buttonFix);
input = input.replace(expression, function(data, match1) {
App.selections.buttonWrapper.append(App.selections.buttonInfo);
if (match1.match(/\d/)) { // Is a number
return "iOS " + match1;
}
Copiar
Copiado
Copiar
Copiado
return "iOS" + match1
;
// Insert button
}
);
App.selections.redoButton.after(App.selections.buttonWrapper)
;
// Insert spacer
App.selections.redoButton.after(App.globals.spacerHTML
);
Copiar
Copiado
Copiar
Copiado
// Default: just replace it with the indicated replacement
// Attach the event listener to the button
} else {
App.selections.buttonFix.click(App.funcs.fixEvent);
input = input.replace(expression, replacement);
}
Copiar
Copiado
Copiar
Copiado
// Check whether anything was changed
App.selections.helpButton.css({
if (input === originalInput) {
'padding': '0px'
return null;
});
} else {
App.selections.buttonWrapper.css({
// Return a dictionary with the reasoning for the fix and what is edited (used later to prevent duplicates in the edit summary)
'position': 'relative',
return {
'left': '430px',
reason: reasoning,
'padding-top': '2%'
fixed: input
});
};
App.selections.buttonFix.css({
}
'position': 'static',
} else {
'float': 'left',
// If nothing needs to be fixed, return null
'border-width': '0px',
return null;
'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'
}
);
}
;
Copiar
Copiado
Copiar
Copiado
// Omit code
App.funcs.
makeDiffTable
= function(
) {
App.funcs.
omitCode
= function(
str, type
) {
App.selections.diffTable = $('<table class="diffTable"/>');
str = str.replace(App.globals.checks[type], function(match) {
App.selections.editor.append(
App.
selections.diffTable
);
App.globals.replacedStrings[type].push(match);
};
return
App.
globals.placeHolders[type];
}
);
return str;
};
Copiar
Copiado
Copiar
Copiado
// Replace code
App.funcs.
fixEvent
= function(
e
) {
App.funcs.
replaceCode
= function(
str, type
) {
if (e) e.preventDefault();
for (var i = 0; i < App.globals.replacedStrings[type].length; i++) {
// Refresh item population
str = str.replace(App.globals.placeHolders[type],
App.funcs.popOriginals();
App.
globals.replacedStrings[type][i]
);
App.
funcs.popItems(
);
}
// Pipe data through editing modules
return str;
App.pipe(App.items, App.globals.pipeMods, App.globals.order);
};
};
Copiar
Copiado
Copiar
Copiado
// Eliminate duplicates in array (awesome method I found on SO, check it out!)
App.funcs.
diff
= function(
) {
// From AstroCB: the original structure of the edit formation prevents duplicates.
App.selections.diffTable.empty();
// Unless you changed that structure somehow, this shouldn't be needed.
App.funcs.
eliminateDuplicates
= function(
arr
) {
var i, len = arr.length,
out = [],
obj = {};
Copiar
Copiado
Copiar
Copiado
for (i = 0; i < len; i++) {
function maakRij(x, y, type, rij) {
obj[arr[i]] = 0;
}
for (i in obj) {
if (obj.hasOwnProperty(i)) { // Prevents messiness of for..in statements
out.push(i);
}
}
return out;
};
Copiar
Copiado
Copiar
Copiado
App.funcs.applyListeners = function() { // Removes default Stack Exchange listeners; see https://github.com/AstroCB/Stack-Exchange-Editor-Toolkit/issues/43
var tr = $('<tr/>');
function removeEventListeners(e) {
if (e.which === 13) {
if (type === '+') tr.addClass('add');
if (e.metaKey || e.ctrlKey) {
if (type === '-') tr.addClass('del');
// CTRL/CMD + Enter -> Activate the auto-editor
App.selections.buttonFix.click();
tr.append($('<td class="codekolom">' + y + '</td>'));
this.focus();
tr.append($('<td class="codekolom">' + x + '</td>'));
} else {
tr.append($('<td class="bredecode">' + type + ' ' + rij.replace(/\</g, '<') + '</td>'));
// It's impossible to remove the event listeners, so we have to clone the element without any listeners
var elClone = this.cloneNode(true);
App.selections.diffTable.append(tr);
this.parentNode.replaceChild(elClone,
}
this);
App.selections.submitButton.click();
function getDiff(matrix, a1, a2, x, y) {
}
if (x > 0 && y > 0 && a1[y - 1] === a2[x - 1]) {
getDiff(matrix, a1, a2, x - 1, y - 1);
maakRij(x, y, ' ', a1[y - 1]);
} else {
if (x > 0 && (y === 0 || matrix[y][x - 1] >= matrix[y - 1][x])) {
getDiff(matrix, a1, a2, x - 1, y);
maakRij(x, '', '+', a2[x - 1]);
} else if (y > 0 && (x === 0 || matrix[y][x - 1] < matrix[y - 1][x])) {
getDiff(matrix, a1, a2, x, y - 1);
maakRij('', y, '-', a1[y - 1], '');
} else {
return;
}
}
}
}
Copiar
Copiado
Copiar
Copiado
// Tags box
}
App.selections.tagField.keydown(removeEventListeners);
// Edit summary box
App.selections.summaryBox.keydown(removeEventListeners);
};
Copiar
Copiado
Copiar
Copiado
// Wait for relevant dynamic content to finish loading
App.funcs.dynamicDelay = function(callback, id, inline) {
var a1 =
App.
originals.body.split('\n'
);
if (inline) { // Inline editing
var a2 =
App.
items.body.split('\n');
setTimeout(function() {
App.
selections.buttonBar = $('#wmd-button-bar-' + id
);
var matrix = new Array(a1.length + 1
);
App.
selections.buttonBar.unbind();
var x, y;
setTimeout(function() {
for (y = 0; y < matrix.length; y++) {
callback();
matrix[y]
=
new Array(a2.length + 1)
;
}, 0);
}, 500
);
} else { // Question page editing
App.selections.buttonBar = $('#wmd-button-bar-' + id);
// 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
;
Copiar
Copiado
Copiar
Copiado
// Run asynchronously - this lets the bar finish updating be
for
e continuing
for
(x = 0; x < matrix[y].length; x++
) {
setTimeout(function(
) {
matrix[y][x] = 0;
callback();
}, 0);
}
});
}
}
Copiar
Copiado
Copiar
Copiado
};
}
// Populate or refresh DOM selections
App.funcs.popSelections = function() {
App.selections.redoButton = $('#wmd-redo-button-' + App.globals.questionNum);
App.selections.bodyBox = $("#wmd-input-" + App.globals.questionNum);
App.selections.titleBox = $(".ask-title-field");
App.selections.summaryBox = $("#edit-comment-" + App.globals.questionNum);
App.selections.tagField = $($(".tag-editor")[0]);
App.selections.submitButton = $("#submit-button-" + App.globals.questionNum);
};
// Populate edit item sets from DOM selections
App.funcs.popItems = function() {
App.items[0] = {
title: App.selections.titleBox.val(),
body: App.selections.bodyBox.val(),
summary: ''
};
};
// Insert editing button(s)
App.funcs.createButton = function() {
// Insert button
App.selections.redoButton.after(App.globals.buttonHTML);
// Insert spacer
App.selections.redoButton.after(App.globals.spacerHTML);
// Add new elements to selections
App.selections.buttonWrapper = $('#ToolkitButtonWrapper');
App.selections.buttonFix = $('#ToolkitFix');
App.selections.buttonInfo = $('#ToolkitInfo');
};
// Style button
App.funcs.styleButton = function() {
var buttonCSS = {
'position': 'relative',
'left': '430px',
'padding-top': '2%'
};
$("#wmd-help-button-" + App.globals.questionNum).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'
});
Copiar
Copiado
Copiar
Copiado
App.selections.buttonFix.hover(function(
) {
for (y = 1; y < matrix.length; y++
) {
App.globals.infoContent = App.selections.buttonInfo.text();
for (x = 1; x < matrix[y].length; x++) {
App.selections.buttonInfo.text('Fix the content!');
if (a1[y - 1] === a2[x - 1]) {
App.selections.buttonFix.css({
matrix[y][x] = 1 + ma
'background-image': 'url("//i.imgur.com/d5ZL09o.png")'
});
}, function() {
App.selections.buttonInfo.text(App.globals.infoContent);
App.s
Diferenças salvas
Texto original
Abrir arquivo
// ==UserScript== // @name Stack-Exchange-Editor-Toolkit // @author Cameron Bernhardt (AstroCB) // @developer Jonathan Todd (jt0dd) // @developer sathyabhat // @contributor Unihedron // @license MIT // @namespace http://github.com/AstroCB // @version 1.5.1 // @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/* // @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== (function(){ "use strict"; var main = function() { // Define app namespace var App = {}; // 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'; // Populate global data // Get url for question id used in id and class names App.globals.URL = window.location.href; // Get question num from URL App.globals.questionNum = App.globals.URL.match(/\/(\d+)\//g); if (App.globals.questionNum) { App.globals.questionNum = App.globals.questionNum[0].split("/").join(""); } // Define variables for later use App.globals.barReady = false; App.globals.editsMade = false; App.globals.editCount = 0; App.globals.infoContent = ''; App.globals.spacerHTML = '<li class="wmd-spacer wmd-spacer3" id="wmd-spacer3-' + App.globals.questionNum + '" style="left: 400px !important;"></li>'; App.globals.buttonHTML = '<div id="ToolkitButtonWrapper"><button class="wmd-button" id="ToolkitFix"></button><div id="ToolkitInfo"></div></div>'; 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 pronoun 'I' is capitalized" }, 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" }, caps: { 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" } }; // 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) { // 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) { App.globals.editCount++; // Later, this will store what is removed for the first case var phrase; // Store the original input text var originalInput = input; // 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 === "") { input = input.replace(expression, function(data, match1) { // Save what is removed for the edit summary (see below) phrase = match1; // 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); // 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); } // Check whether anything was changed if (input === originalInput) { return null; } else { // 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, id, inline) { if (inline) { // Inline editing setTimeout(function() { App.selections.buttonBar = $('#wmd-button-bar-' + id); App.selections.buttonBar.unbind(); setTimeout(function() { callback(); }, 0); }, 500); } else { // Question page editing App.selections.buttonBar = $('#wmd-button-bar-' + id); // 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() { App.selections.redoButton = $('#wmd-redo-button-' + App.globals.questionNum); App.selections.bodyBox = $("#wmd-input-" + App.globals.questionNum); App.selections.titleBox = $(".ask-title-field"); App.selections.summaryBox = $("#edit-comment-" + App.globals.questionNum); App.selections.tagField = $($(".tag-editor")[0]); App.selections.submitButton = $("#submit-button-" + App.globals.questionNum); }; // Populate edit item sets from DOM selections App.funcs.popItems = function() { App.items[0] = { title: App.selections.titleBox.val(), body: App.selections.bodyBox.val(), summary: '' }; }; // Insert editing button(s) App.funcs.createButton = function() { // Insert button App.selections.redoButton.after(App.globals.buttonHTML); // Insert spacer App.selections.redoButton.after(App.globals.spacerHTML); // Add new elements to selections App.selections.buttonWrapper = $('#ToolkitButtonWrapper'); App.selections.buttonFix = $('#ToolkitFix'); App.selections.buttonInfo = $('#ToolkitInfo'); }; // Style button App.funcs.styleButton = function() { var buttonCSS = { 'position': 'relative', 'left': '430px', 'padding-top': '2%' }; $("#wmd-help-button-" + App.globals.questionNum).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(); if (!App.globals.editsMade) { // 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); if (App.selections.summaryBox.val()) { data[0].summary = " " + data[0].summary; // Add a leading space if there's something already in the box } App.selections.summaryBox.val(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.editCount + ' changes made'; App.selections.buttonInfo.text(App.globals.editCount + ' changes made'); }; }; // 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(inline, targetID) { // Check if there was an ID passed (if not, use question ID from URL); if (!targetID) { targetID = App.globals.questionNum; } 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(); }, targetID, inline); }; 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.numReasons] = fix.reason; data[0].body = fix.fixed; App.globals.numReasons++; 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.numReasons] = fix.reason; App.globals.numReasons++; App.edits[j].fixed = true; } } } // Quickly focus the summary field to show generated edit summary, and then jump back App.selections.summaryBox.focus(); // Asynchronous to get in both focuses setTimeout(function() { if (App.globals.lastSelectedElement) { App.globals.lastSelectedElement.focus(); } else { window.scrollTo(0,0); } }, 0); } // Eliminate duplicate reasons App.globals.reasons = App.funcs.eliminateDuplicates(App.globals.reasons); for (var z = 0; z < App.globals.reasons.length; z++) { // Check that summary is not getting too long if (data[0].summary.length < 200) { // Capitalize first letter if (z === 0) { data[0].summary += App.globals.reasons[z][0].toUpperCase() + App.globals.reasons[z].substring(1); // Post rest of reasons normally } else { data[0].summary += App.globals.reasons[z]; } // Not the last reason if (z !== App.globals.reasons.length - 1) { data[0].summary += "; "; // If at end, punctuate } else { data[0].summary += "."; } } } return data; }; setTimeout(function() { // Allow post to load entirely if ($(".edit-post")[0]) { // User has editing privileges; wait for button press $(".edit-post").click(function(e) { App.init(true, e.target.href.match(/\d/g).join("")); // If there are multiple posts, we need to pass the post ID }); } else if ($(".reviewable-post")[0]) { // H&I review queue App.globals.questionNum = $(".reviewable-post")[0].getAttribute("class").match(/\d/g).join(""); $($(".review-actions")[0].children[0]).click(function(e) { App.init(true, App.globals.questionNum); }); } else { // User does not have editing privileges or is editing on question page; start immediately App.init(false); } }, 1000); // Only set when running tests if (window.mocha) { window.App = App; } }; // Inject the main script var script = document.createElement('script'); script.type = "text/javascript"; script.textContent = '(' + main.toString() + ')();'; document.body.appendChild(script); })();
Texto alterado
Abrir arquivo
// ==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.25 // @description Fix common grammar/usage annoyances on Stack Exchange posts with a click // @include /^https?://\w*.?(stackoverflow|stackexchange|serverfault|superuser|askubuntu|stackapps)\.com/(questions|posts|review)/(?!tagged|new).*/ // ==/UserScript== (function() { "use strict"; function extendEditor(root) { var App = {}; // Place edit items here App.items = {}; App.originals = {}; // Place selected jQuery items here App.selections = {}; // Place "global" app data here App.globals = {}; // Place "helper" functions here App.funcs = {}; // True to display rule names in Edit Summary App.globals.showRules = false; //Preload icon alt var SEETicon = new Image(); SEETicon.src = '//i.imgur.com/d5ZL09o.png'; App.globals.root = root; App.globals.spacerHTML = '<li class="wmd-spacer wmd-spacer3" id="wmd-spacer3" style="left: 400px !important;"></li>'; App.globals.reasons = {}; App.globals.replacedStrings = { "auto": [], "quote": [], "inline": [], "block": [], "links": [], "tags": [] }; App.globals.placeHolders = { "auto": "_xAutoxInsertxTextxPlacexHolder_", "quote": "_xBlockxQuotexPlacexHolderx_", "inline": "_xCodexInlinexPlacexHolderx_", "block": "_xCodexBlockxPlacexHolderx_", "links": "_xLinkxPlacexHolderx_", "tags": "_xTagxPlacexHolderx_" }; App.globals.placeHolderChecks = { "auto": /_xAutoxInsertxTextxPlacexHolder_/gi, "quote": /_xBlockxQuotexPlacexHolderx_/gi, "inline": /_xCodexInlinexPlacexHolderx_/gi, "block": /_xCodexBlockxPlacexHolderx_/gi, "links": /_xLinkxPlacexHolderx_/gi, "tags": /_xTagxPlacexHolderx_/gi }; App.globals.checks = { // https://regex101.com/r/cI6oK2/1 automatically inserted text "auto": /[^]*\<\!\-\- End of automatically inserted text \-\-\>/g, // https://regex101.com/r/fU5lE6/1 blockquotes "quote": /^\>(?:(?!\n\n)[^])+/gm, // https://regex101.com/r/lL6fH3/1 single-line inline code "inline": /`[^`\n]+`/g, // https://regex101.com/r/eC7mF7/1 code blocks and multiline inline code. "block": /`[^`]+`|(?:(?:[ ]{4}|[ ]{0,3}\t).+(?:[\r\n]?(?!\n\S)(?:[ ]+\n)*)+)+/g, // https://regex101.com/r/tZ4eY3/5 links and link-sections "links": /\[[^\]\n]+\](?:\([^\)\n]+\)|\[[^\]\n]+\])|(?: (?:\[\d\]): \w*:+\/\/.*\n*)+|(?!.net)(?:\/\w+|.:\\|\.[^ \n\r.]+|\w+:\/\/)[^\s)]*/g, // tags and html comments TODO: needs test "tags": /\<[\/a-z]+\>|\<\!\-\-[^>]+\-\-\>/g }; // Assign modules here App.globals.pipeMods = {}; // Define order in which mods affect here App.globals.order = ["omit", "codefix", "edit", "replace"]; // Define edit rules App.edits = { // All caps noneedtoyell: { expr: /^((?=.*[A-Z])[^a-z]*)$/g, replacement: function(input) { return input.trim().substr(0, 1).toUpperCase() + input.trim().substr(1).toLowerCase(); }, reason: 'no need to yell' }, // Trademark capitalization so: { expr: /\bstack\s*overflow\b/gi, replacement: "Stack Overflow", reason: "'Stack Overflow' is the legal name" }, se: { expr: /\bstack\s*exchange\b/gi, replacement: "Stack Exchange", reason: "'Stack Exchange' is the legal name" }, expansionSO: { expr: /([^\b\w.]|^)SO\b/g, replacement: "$1Stack Overflow", reason: "'Stack Overflow' is the legal name" }, expansionSE: { expr: /([^\b\w.]|^)SE\b/g, replacement: "$1Stack Exchange", reason: "'Stack Exchange' is the legal name" }, javascript: { expr: /([^\b\w.]|^)(javascript|js)\b/gi, replacement: "$1JavaScript", reason: "trademark capitalization" }, jsfiddle: { expr: /\bjsfiddle\b/gi, replacement: "JSFiddle", reason: "trademark capitalization" }, jquery: { expr: /\bjquery\b/gi, replacement: "jQuery", reason: "trademark capitalization" }, angular: { expr: /\bangular(?:js)?\b/gi, replacement: "AngularJS", reason: "trademark capitalization" }, html: { expr: /([^\b\w.]|^)html(\d)?\b/gi, replacement: "$1HTML$2", reason: "trademark capitalization" }, css: { expr: /([^\b\w.]|^)css\b/gi, replacement: "$1CSS", reason: "trademark capitalization" }, json: { expr: /\bjson\b/gi, replacement: "JSON", reason: "acronym capitalization" }, ajax: { expr: /\bajax\b/gi, replacement: "AJAX", reason: "acronym capitalization" }, php: { expr: /([^\b\w.]|^)php\b/gi, replacement: "$1PHP", reason: "trademark capitalization" }, voting: { expr: /\b(down|up)\Wvot/gi, replacement: "$1vote", reason: "the proper spelling (despite the tag name) is '$1vote' (one word)" }, c: { expr: /\bc\b([#+]+)?/gi, replacement: "C$1", reason: "trademark capitalization" }, java: { expr: /\bjava\b/gi, replacement: "Java", reason: "trademark capitalization" }, sql: { expr: /([^\b\w.]|^)sql\b/gi, replacement: "$1SQL", reason: "trademark capitalization" }, sqlite: { expr: /\bsqlite\s*([0-9]*)\b/gi, replacement: "SQLite $2", reason: "trademark capitalization" }, android: { expr: /\bandroid\b/gi, replacement: "Android", reason: "trademark capitalization" }, oracle: { expr: /\boracle\b/gi, replacement: "Oracle", reason: "trademark capitalization" }, windows: { // https://regex101.com/r/jF9zK1/5 expr: /\b(?:win|windows)\s+(2k|[0-9.]+|ce|me|nt|xp|vista|server)|(?:win|windows)\b/gi, replacement: function(match, ver) { ver = !ver ? '' : ver.replace(/ce/i, ' CE') .replace(/me/i, ' ME') .replace(/nt/i, ' NT') .replace(/xp/i, ' XP') .replace(/2k/i, ' 2000') .replace(/vista/i, ' Vista') .replace(/server/i, ' Server'); return 'Windows' + ver; }, reason: "trademark capitalization" }, linux: { expr: /\blinux\b/gi, replacement: "Linux", reason: "trademark capitalization" }, wordpress: { expr: /\bwordpress\b/gi, replacement: "WordPress", reason: "trademark capitalization" }, google: { expr: /\bgoogle\b/gi, replacement: "Google", reason: "trademark capitalization" }, mysql: { expr: /\bmysql\b/gi, replacement: "MySQL", reason: "trademark capitalization" }, apache: { expr: /\bapache\b/gi, replacement: "Apache", reason: "trademark capitalization" }, git: { expr: /\bgit\b/gi, replacement: "Git", reason: "trademark capitalization" }, github: { expr: /\bgithub\b/gi, replacement: "GitHub", reason: "trademark capitalization" }, facebook: { expr: /\bfacebook\b/gi, replacement: "Facebook", reason: "trademark capitalization" }, python: { expr: /\bpython\b/gi, replacement: "Python", reason: "trademark capitalization" }, urli: { expr: /\b(ur[li])\b/gi, replacement: function(match) { return match.toUpperCase(); }, reason: "acronym capitalization" }, ios: { expr: /\bios\b/gi, replacement: "iOS", reason: "trademark capitalization" }, iosnum: { expr: /\bios([0-9])\b/gi, replacement: "iOS $1", reason: "trademark capitalization" }, ubunto: { expr: /\b[uoa]*b[uoa]*[tn][oua]*[tnu][oua]*\b/gi, replacement: "Ubuntu", reason: "trademark capitalization" }, vbnet: { expr: /(?:vb)?(?:\.net|\s?[0-9]+)\s?(?:framework|core)?/gi, replacement: function(str) { return str.replace(/vb/i, 'VB') .replace(/net/i, 'NET') .replace(/framework/i, 'Framework') .replace(/core/i, 'Core'); }, reason: "trademark capitalization" }, regex: { expr: /\bregex(p)?/gi, replacement: "RegEx$1", reason: "trademark capitalization" }, // Noise reduction editupdate: { // https://regex101.com/r/tT2pK6/2 expr: /(?!(?:edit|update)\w*\s*[^:]*$)(?:^\**)(edit|update)\w*(\s*#?[0-9]+)?:?(?:\**):?/gmi, replacement: "", reason: "noise reduction" }, hello: { // TODO: Update badsentences (new) to catch everything hello (old) did. expr: /(?:^|\s)(hi\s+guys|hi|hello|good\s(?:evening|morning|day|afternoon))(?:\.|!|\ )/gmi, replacement: "", reason: "noise reduction" }, badwords: { expr: /[^\n.!?:]*\b(?:th?anks?|th(?:an)?x|tanx|folks?|kind(?:est|ly)|first\s*question)\b[^,.!?\n]*[,.!?]*/gi, replacement: "", reason: "noise reduction" }, badphrases: { expr: /[^\n.!?:]*(?:h[ea]lp|hope|appreciate|pl(?:ease|z|s))[^.!?\n]*(?:helps?|appreciated?)[^,.!?\n]*[,.!?]*/gi, replacement: "", reason: "noise reduction" }, imnew: { expr: /(?! )[\w\s]*\bi[' ]?a?m +(?:kinda|really) *new\w* +(?:to|in) *\w* *(?:and|[;,.!?])? */gi, replacement: "", reason: "noise reduction" }, salutations: { expr: /[\r\n]*(regards|cheers?),?[\t\f ]*[\r\n]?\w*\.?/gi, replacement: "", reason: "noise reduction" }, // Grammar and spelling apostrophe_d: { expr: /\b(he|she|who|you)[^\w']*(d)\b/gi, replacement: "$1'$2", reason: "grammar and spelling" }, apostrophe_ll: { expr: /\b(they|what|who|you)[^\w']*(ll)\b/gi, replacement: "$1'$2", reason: "grammar and spelling" }, apostrophe_re: { expr: /\b(they|what|you)[^\w']*(re)\b/gi, replacement: "$1'$2", reason: "grammar and spelling" }, apostrophe_s: { expr: /\b(he|she|that|there|what|where)[^\w']*(s)\b/gi, replacement: "$1'$2", reason: "grammar and spelling" }, apostrophe_t: { expr: /\b(aren|can|didn|doesn|don|hasn|haven|isn|mightn|mustn|shan|shouldn|won|wouldn)[^\w']*(t)\b/gi, replacement: "$1'$2", reason: "grammar and spelling" }, prolly: { expr: /\bproll?y\b/gi, replacement: "probably", reason: "grammar and spelling" }, i: { expr: /\bi('|\b)/g, // i or i-apostrophe replacement: "I", reason: "grammar and spelling" }, im: { expr: /\bim\b/gi, replacement: "I'm", reason: "grammar and spelling" }, ive: { expr: /\bive\b/gi, replacement: "I've", reason: "grammar and spelling" }, ur: { expr: /\bur\b/gi, replacement: "your", // May also be "you are", but less common on SO reason: "grammar and spelling" }, u: { expr: /\bu\b/gi, replacement: "you", reason: "grammar and spelling" }, gr8: { expr: /\bgr8\b/gi, replacement: "great", reason: "grammar and spelling" }, allways: { expr: /\b(a)llways\b/gi, replacement: "$1lways", reason: "grammar and spelling" }, // Punctuation & Spacing come last firstcaps: { // https://regex101.com/r/qR5fO9/12 // This doesn't work quite right, because is finds all sentences, not just ones needing caps. //expr: /(?:(?!\n\n)[^\s.!?]+[ ]*)+([.!?])*[ ]*/g, expr: /((?!\n\n)(?:[^?.!])*([?.!]|\n\n)?\)*)/gm, replacement: function(str, endpunc) { if (str === "undefined") return ''; console.log('str: '+str); // https://regex101.com/r/bL9xD7/1 find and capitalize first letter return str.replace(/^(\W*)([a-z])(.*)/g, function(sentence, pre, first, post) { if (!pre) pre = ''; if (!post) post = ''; console.log('sentence ('+sentence+') pre ('+pre+') first ('+first+') post ('+post+') endpunc ('+endpunc+')'); var update = pre + first.toUpperCase() + post// + (!endpunc && /\w/.test(post.substr(-1)) ? '.' : ''); console.log('update ('+update+')'); return update; }); }, reason: "Caps at start of sentences" }, multiplesymbols: { // https://regex101.com/r/bE9zM6/1 expr: /([^\w\s*#.\-_])\1{1,}/g, replacement: "$1", reason: "punctuation & spacing" }, spacesbeforesymbols: { expr: /\s+([.,!?;:])(?!\w)/g, replacement: "$1", reason: "punctuation & spacing" }, multiplespaces: { // https://regex101.com/r/hY9hQ3/1 expr: /[ ]{2,}(?!$)/g, replacement: " ", reason: "punctuation & spacing" } }; // 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 matches = input.match(expression); if (!matches) return false; console.log(JSON.stringify(matches)) var count = matches.length; // # replacements to do var tmpinput = input; input = input.replace(expression, function() { var matches = [].slice.call(arguments, 0, -2); reasoning = reasoning.replace(/[$](\d)+/g, function() { var phrases = [].slice.call(arguments, 0, -2); var phrase = matches[phrases[1]]; return phrase ? phrase : ''; }); return arguments[0].replace(expression, replacement); }); if (input !== tmpinput) { return { reason: reasoning, fixed: String(input).trim(), count: count }; } else return false; }; 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(); } else { // It's possible to remove the event listeners, because of the way outerHTML works. this.outerHTML = this.outerHTML; App.selections.submitButton.click(); } } } // Tags box App.selections.tagField.keydown(removeEventListeners); // Edit summary box App.selections.summary.keydown(removeEventListeners); }; // Populate or refresh DOM selections App.funcs.popSelections = function() { App.selections.redoButton = App.globals.root.find('[id^="wmd-redo-button"]'); App.selections.body = App.globals.root.find('[id^="wmd-input"]'); App.selections.title = App.globals.root.find('[class*="title-field"]'); App.selections.summary = App.globals.root.find('[id^="edit-comment"]'); App.selections.tagField = App.globals.root.find(".tag-editor"); App.selections.submitButton = App.globals.root.find('[id^="submit-button"]'); App.selections.helpButton = App.globals.root.find('[id^="wmd-help-button"]'); App.selections.editor = App.globals.root.find('.post-editor'); }; // Populate edit item sets from DOM selections App.funcs.popItems = function() { var i = App.items, s = App.selections; ['title', 'body', 'summary'].forEach(function(v) { i[v] = String(s[v].val()).trim(); }); }; // Populate original item sets from DOM selections App.funcs.popOriginals = function() { var i = App.originals, s = App.selections; ['title', 'body', 'summary'].forEach(function(v) { i[v] = String(s[v].val()).trim(); }); }; // Insert editing button(s) App.funcs.createButton = function() { if (!App.selections.redoButton.length) return false; App.selections.buttonWrapper = $('<div class="ToolkitButtonWrapper"/>'); App.selections.buttonFix = $('<button class="wmd-button ToolkitFix" title="Fix the content!" />'); App.selections.buttonInfo = $('<div class="ToolkitInfo">'); // Build the button App.selections.buttonWrapper.append(App.selections.buttonFix); App.selections.buttonWrapper.append(App.selections.buttonInfo); // Insert button App.selections.redoButton.after(App.selections.buttonWrapper); // Insert spacer App.selections.redoButton.after(App.globals.spacerHTML); // Attach the event listener to the button App.selections.buttonFix.click(App.funcs.fixEvent); App.selections.helpButton.css({ 'padding': '0px' }); App.selections.buttonWrapper.css({ 'position': 'relative', 'left': '430px', 'padding-top': '2%' }); 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.funcs.makeDiffTable = function() { App.selections.diffTable = $('<table class="diffTable"/>'); App.selections.editor.append(App.selections.diffTable); }; App.funcs.fixEvent = function(e) { if (e) e.preventDefault(); // Refresh item population App.funcs.popOriginals(); App.funcs.popItems(); // Pipe data through editing modules App.pipe(App.items, App.globals.pipeMods, App.globals.order); }; App.funcs.diff = function() { App.selections.diffTable.empty(); function maakRij(x, y, type, rij) { var tr = $('<tr/>'); if (type === '+') tr.addClass('add'); if (type === '-') tr.addClass('del'); tr.append($('<td class="codekolom">' + y + '</td>')); tr.append($('<td class="codekolom">' + x + '</td>')); tr.append($('<td class="bredecode">' + type + ' ' + rij.replace(/\</g, '<') + '</td>')); App.selections.diffTable.append(tr); } function getDiff(matrix, a1, a2, x, y) { if (x > 0 && y > 0 && a1[y - 1] === a2[x - 1]) { getDiff(matrix, a1, a2, x - 1, y - 1); maakRij(x, y, ' ', a1[y - 1]); } else { if (x > 0 && (y === 0 || matrix[y][x - 1] >= matrix[y - 1][x])) { getDiff(matrix, a1, a2, x - 1, y); maakRij(x, '', '+', a2[x - 1]); } else if (y > 0 && (x === 0 || matrix[y][x - 1] < matrix[y - 1][x])) { getDiff(matrix, a1, a2, x, y - 1); maakRij('', y, '-', a1[y - 1], ''); } else { return; } } } var a1 = App.originals.body.split('\n'); var a2 = App.items.body.split('\n'); var matrix = new Array(a1.length + 1); var x, y; for (y = 0; y < matrix.length; y++) { matrix[y] = new Array(a2.length + 1); for (x = 0; x < matrix[y].length; x++) { matrix[y][x] = 0; } } for (y = 1; y < matrix.length; y++) { for (x = 1; x < matrix[y].length; x++) { if (a1[y - 1] === a2[x - 1]) { matrix[y][x] = 1 + matrix[y - 1][x - 1]; } else { matrix[y][x] = Math.max(matrix[y - 1][x], matrix[y][x - 1]); } } } try { getDiff(matrix, a1, a2, x - 1, y - 1); } catch (e) { alert(e); } }; // Handle pipe output App.funcs.output = function(data) { App.selections.title.val(data.title); App.selections.body.val(data.body); App.selections.summary.val(data.summary); App.selections.summary.focus(); App.selections.editor.append(App.funcs.diff()); StackExchange.MarkdownEditor.refreshAllPreviews(); App.selections.buttonInfo.text(App.globals.changes + (App.globals.changes>1 ? ' changes' : ' change')+' made'); }; // Pipe data through modules in proper order, returning the result App.pipe = function(data, mods, order) { var modName; for (var i in order) { if (order.hasOwnProperty(i)) { modName = order[i]; mods[modName](data); } } App.funcs.output(data); }; App.globals.pipeMods.omit = function(data) { if (!data.body) return false; for (var type in App.globals.checks) { data.body = data.body.replace(App.globals.checks[type], function(match) { App.globals.replacedStrings[type].push(match); return App.globals.placeHolders[type]; }); } return data; }; App.globals.pipeMods.codefix = function() { var replaced = App.globals.replacedStrings.block, str; for (var i in replaced) { // https://regex101.com/r/tX9pM3/1 https://regex101.com/r/tX9pM3/2 https://regex101.com/r/tX9pM3/3 if (/^`[^]+`$/.test(replaced[i])) replaced[i] = /(?!`)((?!`)[^])+/.exec(replaced[i])[1].replace(/(.+)/g, ' $1'); } }; App.globals.pipeMods.replace = function(data) { if (!data.body) return false; for (var type in App.globals.checks) { var i = 0; data.body = data.body.replace(App.globals.placeHolderChecks[type], function(match) { return App.globals.replacedStrings[type][i++]; }); } 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.body.animate({ backgroundColor: '#c8ffa7' }, 10); App.selections.body.animate({ backgroundColor: '#fff' }, 1000); // List of fields to be edited var fields = {body:'body',title:'title'}; // Loop through all editing rules for (var j in App.edits) { for (var field in fields) { if (App.edits.hasOwnProperty(j)) { var fix = App.funcs.fixIt(data[field], App.edits[j].expr, App.edits[j].replacement, App.edits[j].reason); if (fix) { // HACK ALERT if (j === 'firstcaps') fix.count = 1; if (!App.globals.reasons.hasOwnProperty(fix.reason)) { App.globals.reasons[fix.reason] = {reason:fix.reason, editId:j, count:fix.count}; } else { App.globals.reasons[fix.reason].count += fix.count; } data[field] = fix.fixed; App.edits[j].fixed = true; } } } } // If there are no reasons, exit if (App.globals.reasons == {}) return false; // We need a place to store the reasons being applied to the summary. var reasons = []; App.globals.changes = 0; for (var z in App.globals.reasons) { // For each type of change made, add a reason string with the reason text, // optionally the rule ID, and the number of repeats if 2 or more. reasons.push(App.globals.reasons[z].reason + (App.globals.showRules ? ' ['+ App.globals.reasons[z].editId +']' : '') + ((App.globals.reasons[z].count > 1) ? ' ('+App.globals.reasons[z].count+')' : '') ); App.globals.changes += App.globals.reasons[z].count; } var reasonStr = reasons.join('; ')+'.'; // Unique reasons separated by ; and terminated by . reasonStr = reasonStr.charAt(0).toUpperCase() + reasonStr.slice(1); // Cap first letter. if (!data.summaryOrig) data.summaryOrig = data.summary.trim(); // Remember original summary if (data.summaryOrig.length) data.summaryOrig = data.summaryOrig + ' '; data.summary = data.summaryOrig + reasonStr; // Limit summary to 300 chars if (data.summary.length > 300) data.summary = data.summary.substr(0,300-3) + '...'; return data; }; // Init app App.init = function() { var count = 0; var toolbarchk = setInterval(function(){ //console.log('waiting for toolbar'); if(++count === 10) clearInterval(toolbarchk) if(!App.globals.root.find('.wmd-button-row').length) return; clearInterval(toolbarchk); //console.log('found toolbar'); App.funcs.popSelections(); App.funcs.createButton(); App.funcs.applyListeners(); App.funcs.makeDiffTable(); }, 100); return App; }; return App.init(); } try { var test = window.location.href.match(/.posts.(\d+).edit/); if(test) extendEditor($('form[action^="/posts/' + test[1] + '"]')); else $(document).ajaxComplete(function() { test = arguments[2].url.match(/posts.(\d+).edit-inline/); if(!test) { test = arguments[2].url.match(/review.inline-edit-post/) if(!test) return; test = arguments[2].data.match(/id=(\d+)/); if(!test) return; } extendEditor($('form[action^="/posts/' + test[1] + '"]')); }); if($('#post-form').length) extendEditor($('#post-form')); // This is the styling for the diff output. $('body').append('<style>.diff { max-width: 100%; overflow: auto; } td.bredecode, td.codekolom { padding: 1px 2px; } td.bredecode { width: 100%; padding-left: 4px; white-space: pre-wrap; word-wrap: break-word; } td.codekolom { text-align: right; min-width: 3em; background-color: #ECECEC; border-right: 1px solid #DDD; color: #AAA; } tr.add { background: #DFD; } tr.del { background: #FDD; }</style>'); } catch (e) { console.log(e); } })();
Encontrar Diferença