Back2source

Redirecting to source sites from sites with machine translation, etc.

当前为 2022-05-05 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Back2source
  3. // @version 0.1.94
  4. // @description Redirecting to source sites from sites with machine translation, etc.
  5. // @namespace vladgba
  6. // @author vladgba@gmail.com
  7. // @run-at document-end
  8. // @icon https://www.google.com/s2/favicons?domain=stackoverflow.com
  9. // @homepageURL https://github.com/vladgba/Back2source
  10. // @supportURL https://github.com/vladgba/Back2source/issues
  11. // @grant GM_xmlhttpRequest
  12. // @grant GM_registerMenuCommand
  13. // @grant GM_setValue
  14. // @grant GM_getValue
  15. // @connect api.browser.yandex.ru
  16. // @noframes
  17. // @match *://*.*.nina.az/wiki/*
  18. // @match *://*.360wiki.ru/wiki/*
  19. // @match *://*.8101010108.cn/zh/*
  20. // @match *://*.abcdef.wiki/*
  21. // @match *://*.answacode.com/questions/*
  22. // @match *://*.answer-id.com/*
  23. // @match *://*.answeright.com/*
  24. // @match *://*.antwortenhier.me/*
  25. // @match *://*.ask-ubuntu.ru/questions/*
  26. // @match *://*.askdev.info/questions/*
  27. // @match *://*.askdev.ru/q-*
  28. // @match *://*.askdev.ru/q/*
  29. // @match *://*.askentire.net/q/*-*
  30. // @match *://*.askfrance.me/*
  31. // @match *://*.askubuntu.ru/questions/*
  32. // @match *://*.askvoprosy.com/voprosy/*
  33. // @match *://*.bcqaw.com/*.html
  34. // @match *://*.bildiredi.com/*
  35. // @match *://*.bilee.com/*.html
  36. // @match *://*.buildwiki.ru/wiki/*
  37. // @match *://*.catwolf.org/qs*
  38. // @match *://*.ciupacabra.com/*
  39. // @match *://*.cloud.tencent.com/developer/ask/*
  40. // @match *://*.code-examples.net/*/q/*
  41. // @match *://*.code.i-harness.com/*/q/*
  42. // @match *://*.codefactor.io/repository/*
  43. // @match *://*.codegear.dev/*/questions/*
  44. // @match *://*.codegrepper.com/*
  45. // @match *://*.codeindex.ru/q/*
  46. // @match *://*.codengineering.ru/q/*
  47. // @match *://*.codenong.com/*
  48. // @match *://*.coder.work/article/*
  49. // @match *://*.coderedirect.com/*
  50. // @match *://*.coderoad.in/questions/*
  51. // @match *://*.coderoad.ru/*
  52. // @match *://*.coderoad.wiki/*
  53. // @match *://*.coderquestion.ru/q/*
  54. // @match *://*.codersatellite.com/question-with-identifier-*
  55. // @match *://*.coredump.biz/questions/*
  56. // @match *://*.datewiki.ru/wiki/*
  57. // @match *://*.de-vraag.com/*
  58. // @match *://*.desarrollo-web-br-bd.com/es/*
  59. // @match *://*.developreference.com/*
  60. // @match *://*.devfaq.fr/question/*
  61. // @match *://*.dir.md/*
  62. // @match *://*.donolik.com/*
  63. // @match *://*.e-learn.cn/topic/*
  64. // @match *://*.encyclopaedia.bid/*
  65. // @match *://*.exceptionshub.com/*
  66. // @match *://*.extutorial.com/ask/*
  67. // @match *://*.fixes.pub/*/*.html
  68. // @match *://*.fluffyfables.com/*
  69. // @match *://*.fooobar.com/questions/*
  70. // @match *://*.fullstackuser.com/questions/*
  71. // @match *://*.gaz.wiki/wiki/*
  72. // @match *://*.generacodice.com/*
  73. // @match *://*.giters.com/*
  74. // @match *://*.githubhot.com/*
  75. // @match *://*.githublab.com/*/*
  76. // @match *://*.githubmemory.com/*
  77. // @match *://*.gitrush.ru/*/*/*
  78. // @match *://*.higithub.com/*/*
  79. // @match *://*.hmong.ru/wiki/*
  80. // @match *://*.hmong.wiki/wiki/*
  81. // @match *://*.howtosolves.com/q/*
  82. // @match *://*.html-agility-pack.net/knowledge-base/*
  83. // @match *://*.husl.ru/questions/*
  84. // @match *://*.icode9.com/*
  85. // @match *://*.issue.life/questions/*
  86. // @match *://*.issueantenna.com/*/*
  87. // @match *://*.issueexplorer.com/repo/*/*
  88. // @match *://*.it-brain.online/question/*
  89. // @match *://*.itdaan.com/*
  90. // @match *://*.itnan.ru/post.php*
  91. // @match *://*.itranslater.com/qa/details/*
  92. // @match *://*.javaer101.com/*/*
  93. // @match *://*.jejakjabar.com/wiki/*
  94. // @match *://*.jsrepos.com/*/*
  95. // @match *://*.kompsekret.ru/q/*
  96. // @match *://*.kotaeta.com/*
  97. // @match *://*.legkovopros.ru/questions/*
  98. // @match *://*.lifesaver.codes/answer/*
  99. // @match *://*.livepcwiki.ru/wiki/*
  100. // @match *://*.localcoder.org/*
  101. // @match *://*.mihalicdictionary.org/*
  102. // @match *://*.mlink.in/*
  103. // @match *://*.mlog.club/article/*
  104. // @match *://*.newbedev.com/*
  105. // @match *://*.npmmirror.com/package/*
  106. // @match *://*.overcoder.net/q/*
  107. // @match *://*.overcoder.ru/q/*
  108. // @match *://*.pengembangan-web-mp-pd.com/id/*
  109. // @match *://*.poweruser.guru/*
  110. // @match *://*.prog-help.ru/*
  111. // @match *://*.progi.pro/*
  112. // @match *://*.proubuntu.ru/*/*
  113. // @match *://*.py4u.net/discuss/*
  114. // @match *://*.pythonq.com/*/*/*
  115. // @match *://*.qa-help.ru/*
  116. // @match *://*.qa-stack.pl/*
  117. // @match *://*.qa.1r1g.com/sf/ask/*
  118. // @match *://*.qacode.ru/questions/*
  119. // @match *://*.qarchive.ru/*
  120. // @match *://*.qaru.tech/questions/*
  121. // @match *://*.qarus.ru/*
  122. // @match *://*.qastack.net.bd/*
  123. // @match *://*.quabr.com/*
  124. // @match *://*.quares.ru/?id=*
  125. // @match *://*.question-it.com/questions/*
  126. // @match *://*.recalll.co/*
  127. // @match *://*.reponse-question-developpement-web-bd.com/fr/*
  128. // @match *://*.respuestas.me/*
  129. // @match *://*.risposta-alla-domanda-sullo-sviluppo-web-bd.com/it/*
  130. // @match *://*.ru.encyclopedia.kz/index.php/*
  131. // @match *://*.rudata.ru/wiki/*
  132. // @match *://*.ruphp.com/*.html
  133. // @match *://*.savepearlharbor.com/?p=*
  134. // @match *://*.sbup.com/wiki/*
  135. // @match *://*.serveanswer.com/questions/*
  136. // @match *://*.snyk.io/advisor/npm-package/*
  137. // @match *://*.sobrelinux.info/questions/*
  138. // @match *://*.soinside.com/question/*
  139. // @match *://*.sprosi.pro/questions/*
  140. // @match *://*.sqlite.in/*
  141. // @match *://*.stackanswers.net/questions/*
  142. // @match *://*.stackoom.com/question/*
  143. // @match *://*.stackoverflood.com/*
  144. // @match *://*.stackru.com/questions/*
  145. // @match *://*.stormcrow.dev/*/questions/*
  146. // @match *://*.switch-case.com/*
  147. // @match *://*.techarks.ru/qa/*
  148. // @match *://*.techfeed.net/*
  149. // @match *://*.territorioscuola.it/*
  150. // @match *://*.thinbug.com/q/*
  151. // @match *://*.tra-loi-cau-hoi-phat-trien-web.com/vi/*
  152. // @match *://*.try2explore.com/*
  153. // @match *://*.ubuntugeeks.com/questions/*
  154. // @match *://*.utyatnishna.ru/info/*
  155. // @match *://*.uwenku.com/question/*
  156. // @match *://*.v-resheno.ru/*
  157. // @match *://*.voidcc.com/question/*
  158. // @match *://*.vvikipedla.com/wiki/*
  159. // @match *://*.web-answers.ru/*/*
  160. // @match *://*.web-dev-qa-db-fr.com/fr/*
  161. // @match *://*.web-dev-qa-db-ja.com/ja/*
  162. // @match *://*.web-gaebal-jilmun-dabbyeon-db.com/ko/*
  163. // @match *://*.web-gelistirme-sc.com/tr/*
  164. // @match *://*.webentwicklung-frage-antwort-db.com.de/de/*
  165. // @match *://*.while-do.com/*
  166. // @match *://*.wiki-org.ru/*
  167. // @match *://*.wiki-wiki.ru/wp/*
  168. // @match *://*.wiki.cologne/*
  169. // @match *://*.wiki2.info/*
  170. // @match *://*.wiki2.net/*
  171. // @match *://*.wiki2.online/*
  172. // @match *://*.wiki2.org/*
  173. // @match *://*.wiki2.wiki/wiki/*
  174. // @match *://*.wikichi.ru/wiki/*
  175. // @match *://*.wikidark.ru/*
  176. // @match *://*.wikies.wiki/wiki/*
  177. // @match *://*.wikipe.wiki/wiki/*
  178. // @match *://*.wikipedia-on-ipfs.org/wiki/*
  179. // @match *://*.wikipedia.tel/*
  180. // @match *://*.wikipedia24.ru/*
  181. // @match *://*.wikiredia.ru/*
  182. // @match *://*.wikiroot.ru/question/*
  183. // @match *://*.wikivisually.com/wiki/*
  184. // @match *://*.wikiwand.com/*/*
  185. // @match *://*.wikizero.com/*/*
  186. // @match *://*.xbuba.com/*
  187. // @match *://*.xcv.wiki/*
  188. // @match *://*.xiu2.net/it/details/*
  189. // @match *://*.xszz.org/*/question-*
  190. // @match *://*.ylhow.com/*
  191. // @match *://*.yuanmacha.com/*.html
  192. // @match *://*.zapytay.com/*
  193. // @include *://qastack.tld/*
  194. // ==/UserScript==
  195. (async () => {
  196. 'use strict';
  197.  
  198. var sitecolor = '#333';
  199. var lang = 'ru';
  200. var badCode = false;
  201. var badImgs = false;
  202. var _ = undefined;
  203. var ll, tt;
  204. const _p = location.pathname;
  205. const _ps = _p.split('/');
  206. const _h = location.href;
  207. var _$s = (s) => typeof s === 'string' || s instanceof String; //is_string
  208. var _hp = (p=3) => (ll = location.hostname.split('.')) && ll[ll.length-p];//get_subdomain (host part)
  209. var _t = (s) => document.querySelector(s);
  210. var _c = (r) => r.test(_p);
  211. var _go = (s) => s && window.location.replace(s);
  212. var _hst = (s) => location.hostname.includes(s);
  213. var clr = (c, f) => (sitecolor = (f || sitecolor == '#333') ? c : sitecolor);
  214. var lng = (c, f) => (lang = (f || lang == 'ru') ? c : lang);
  215. var lastPathPart = () => _ps.filter(Boolean).slice(-1)[0];
  216. var bySel = (s, a = 'href') => _t(s)?.getAttribute(a);
  217. var getHeader = (h) => removeAuxiliary(h ? (Array.isArray(h) ? h[0] : textContent(h)) : textContent('h1'));
  218. var getTags = (t) => t ? (Array.isArray(t) ? t[0] : allTexts(t)) : allTexts('.tag');
  219. var mulreplace = (str, a) => a.forEach((v) => (str = str.replace(v[0], v[1]))) || str;
  220. var wiki = (l = 0, p = 2, w = true) => 'https://' + (_$s(l) ? l : _ps[l]) + '.wikipedia.org' + (w ? '/wiki/' : '') + (_$s(p) ? p : _ps[p]);
  221. var prepareSearch = (h, t, s) => promtRedirect(sitecolor, toSearch(h + ' ' + getTags(t).join(' ').replace(/\s+/g, ' '), s), !badCode && allTexts('pre code'), !badImgs && [...new Set([...allAttr('img[src*="://i.stack.imgur.com/"]', 'src'), ...allAttr('a[href*="://i.stack.imgur.com/"]', 'href')])], s);
  222. var transTags = async (t) => (await yaTranslate(allTexts(t).join(' '), lang)).split(' ');
  223. var toSearch = (s, site, i) => (s = dropMarks(s) && s ? `https://google.com/search?q=` + ((site && Array.isArray(site)) ? (site.length < 1 ? '' : `site%3A` + site.join('+OR+site%3A') + `+`) : `site%3Astackexchange.com+OR+site%3Astackoverflow.com+`) + encodeURIComponent(s) + (i ? '&tbm=isch' : '') : null);
  224. var textContent = (s) => Array.isArray(s) ? s[0] : _t(s)?.textContent.trim();
  225. var byNumber = (s, radix) => (s = parseInt(s, radix)) && s > 0 ? _go('https://stackoverflow.com/questions/' + s) : null;
  226. var normalize = (s) => s && ' ' + s.toLowerCase() + ' ';
  227. var pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
  228. var all = (s) => Array.prototype.slice.call(document.querySelectorAll(s));
  229. var allTexts = (s) => all(s).map(a => a.textContent.trim());
  230. var allAttr = (s, t) => all(s).map(a => a[t].trim());
  231. var getAttr = (t, a, r, s = '$1') => (t.hasAttribute(a)) && t.getAttribute(a).replace(r, s);
  232. var dropMarks = (s) => s && s.replace(/\[(на удержании|on hold|duplikować|duplicado|duplicar|duplikat|dublicate|duplicate|дубликат|закрыто|закрытый|closed|geschlossen|zamknięte|cerrado|重复|repeat)\]\s*$/i, '').trim();
  233.  
  234. function _tc (s) {
  235. var allw = ['stackoverflow.com/q','superuser.com/q','mathoverflow.net/q','askubuntu.com/q','stackexchange.com/q'];
  236. var nods = all(s);
  237. for (var nod in nods) for (var pt in allw) if(nods[nod]?.href?.indexOf(allw[pt])>=0) return nods[nod].href;
  238. }
  239.  
  240. function urlByImg(v, s = 'img[src*="/images/content/"]', n = 3) {
  241. var p = _t(s)?.src;
  242. if (!p) return;
  243. var l = (new URL(p)).pathname.split('/')[n];
  244. return l && (v + l);
  245. }
  246.  
  247. var db = JSON.parse(GM_getValue('b2s') || '{}');
  248. for (var y in db) if (location.href == db[y][0]) return _go(db[y][1]);
  249.  
  250. var dfgdr = fetch(`https://api.zcxv.icu/b2s.php?q=get&url=${encodeURIComponent(_h)}`, { credentials: 'omit' })
  251. .then(r => r.json())
  252. .then(r => r.res && r.response && _go(r.response));
  253.  
  254. GM_registerMenuCommand('Redirect', () => {
  255. var re = prompt('Enter source url:');
  256. var dfgdr = re && fetch(
  257. `https://api.zcxv.icu/b2s.php?q=set&url=${encodeURIComponent(_h)}&redir=${encodeURIComponent(re)}`, {
  258. method: 'GET', credentials: 'omit'
  259. });
  260. });
  261.  
  262. function addJS(code){
  263. var scriptElm = document.createElement('script');
  264. var inlineCode = document.createTextNode(code);
  265. scriptElm.appendChild(inlineCode);
  266. document.body.appendChild(scriptElm);
  267. }
  268.  
  269. function filterText(text, rmquotes) {
  270. var out = text.replace(/(\u02B9|\u0374|\u2018|\u201A|\u2039|\u203A|\u201B|\u2019)+/g, '\'').replace(/(\u00AB|\u00BB|\u201E|\u201C|\u201F|\u201D|\u2E42)+/g, '"');
  271. return (rmquotes ? out.replace(/(\'|")+/g, ' ') : out).replace(/ /g, ' ').replace(/(\r|\n)+/g, ' ').replace(/\s\s+/g, ' ').trim().replace(/\.$/, '').trim();
  272. }
  273.  
  274. async function promtRedirect(bgcolor, link, codef, imgf, site) {
  275. const dialog = document.createElement('div');
  276. try {
  277. document.body.appendChild(dialog);
  278. const shadowRoot = dialog.attachShadow ?
  279. dialog.attachShadow({ mode: 'open' }) :
  280. //@ts-ignore
  281. dialog.createShadowRoot && dialog.createShadowRoot();
  282. if (!shadowRoot) throw 'Shadow dom required!';
  283. shadowRoot.innerHTML = `
  284. <style>
  285. :host{
  286. position: fixed;
  287. bottom: 0;
  288. z-index: 16777271;
  289. width: 100%;
  290. color: white;
  291. background-color: ${bgcolor};
  292. }
  293. .m{
  294. padding: 14px;
  295. font-family: Ubuntu,Segoe UI,Optima,Trebuchet MS,-apple-system,BlinkMacSystemFont,sans-serif;
  296. font-size: 14px;
  297. }
  298. #close-btn{
  299. float: right;
  300. cursor: pointer;
  301. }
  302. a{
  303. color: white;
  304. }
  305. .search-icon{
  306. font-size: 24px;
  307. line-height: 0;
  308. text-decoration: none;
  309. }
  310. </style>
  311. <div class="m">[ Back2Source ]
  312. <a id="ok-btn" href="#">Try to find the original question? <span class="search-icon">&#8981;<span></a> ` +
  313. (codef && codef.length > 0 ? `<a href="` + toSearch(codef.join(' '), site) + `">[ByCode]</a>` : ``) +
  314. (imgf && imgf.length > 0 ? `<a href="` + toSearch(imgf.join(' '), site, true) + `">[ByImgs]</a>` : ``) +
  315. `<span id="close-btn">&#10006;</span>
  316. </div>`;
  317. shadowRoot.querySelector('#ok-btn').href = link;
  318. await new Promise((_, reject) => {
  319. shadowRoot.querySelector('#close-btn').addEventListener('click', reject);
  320. });
  321. } finally {
  322. document.body.removeChild(dialog);
  323. }
  324. }
  325.  
  326. //https://yandex.com/dev/translate/doc/dg/concepts/api-overview.html
  327. async function yaTranslate(q, sourceLang, targetLang) {
  328. q = dropMarks(q);
  329. if (!q) return null;
  330. q = 'https://api.browser.yandex.ru/dictionary/translate?statLang=en&targetLang=' + (targetLang ? targetLang : 'en') + '&text=' + encodeURIComponent(q) + (sourceLang ? '&fromLang=' + sourceLang : '')
  331. try {
  332. //dosn't work in chrome
  333. return await fetch(q, {
  334. mode: 'no-cors',
  335. credentials: 'omit'
  336. })
  337. .then(r => r.json())
  338. .then(r => r.text);
  339. } catch (e) {
  340. //works only in tampermonkey
  341. return new Promise((resolve, reject) => {
  342. //@ts-ignore
  343. GM_xmlhttpRequest({
  344. url: q,
  345. responseType: 'json',
  346. anonymous: true,
  347. onload: (xhr) => {
  348. xhr.status === 200 ? resolve(xhr.response.text.replace(/ *\[repeat\]/i, " [duplicate]")) : reject(xhr);
  349. },
  350. onerror: reject
  351. })
  352. })
  353. }
  354. }
  355.  
  356. var auxiliaryRe = null;
  357. function removeAuxiliary(s) {
  358. return s && s.replace(auxiliaryRe || (auxiliaryRe = new RegExp([
  359. 'a', 'an', 'the',
  360. //Conjunctions http://englishgu.ru/soyuzyi-v-angliyskom-yazyike-tablitsa-spisok/
  361. //https://7esl.com/english-conjunctions/
  362. 'according to', 'after', 'against', 'also', 'although', 'and', 'as far as', 'as if', 'as long as', 'as much as', 'as soon as', 'as though', 'as well as', 'as', 'assuming that', 'at last', 'at least', 'because of', 'because', 'before', 'beyond', 'both', 'but', 'by the time', 'either', 'even if', 'even though', 'for', 'from now on', 'from time to time', 'how', 'however', 'if', 'in case', 'in order', 'in spite of', 'in terms of', 'lest', 'like', 'meanwhile', 'moreover', 'neither', 'nevertheless', 'no matter how', 'no matter what', 'no matter when', 'no matter where', 'no matter who', 'no matter why', 'nor', 'not so as', 'not yet', 'now that', 'on behalf of', 'on condition', 'on the contrary', 'on the other hand', 'once', 'only if', 'or', 'otherwise', 'owing to', 'provided that', 'rather than', 'since', 'so that', 'so', 'still', 'than', 'that is why', 'that', 'therefore', 'though', 'thus', 'till', 'unless', 'unlike', 'until', 'what', 'whatever', 'when', 'whenever', 'where', 'whereas', 'wherever', 'whether', 'which', 'whichever', 'while', 'who', 'whoever', 'whom', 'whomever', 'whose', 'with', 'within', 'without', 'yet',
  363. //some of Preposition https://www.englishclub.com/grammar/prepositions-list.htm
  364. //https://www.talkenglish.com/vocabulary/top-50-prepositions.aspx
  365. 'aboard', 'about', 'above', 'across', 'after', 'against', 'along', 'amid', 'among', 'anti', 'around', 'at', 'behind', 'below', 'beneath', 'beside', 'besides', 'beyond', 'but', 'by', 'concerning', 'considering', 'despite', 'down', 'during', 'excepting', 'excluding', 'following', 'for', 'from', 'in', 'including', 'inside', 'into', 'of', 'off', 'on', 'onto', 'opposite', 'out', 'outside', 'over', 'past', 'per', 'regarding', 'since', 'than', 'through', 'throughout', 'to', 'toward', 'towards', 'under', 'underneath', 'unlike', 'until', 'up', 'upon', 'versus', 'via', 'within', 'without',
  366. //some of https://7esl.com/interjections-exclamations/
  367. 'aah', 'ah', 'aha', 'ahem', 'alas', 'argh', 'aw', 'aww', 'bah', 'behold', 'bingo', 'boo', 'bravo', 'brr', 'dear', 'duh', 'eek', 'eh', 'er', 'eww', 'gah', 'gee', 'grr', 'hah', 'hello', 'hey', 'hi', 'hmm', 'huh', 'hullo', 'humph', 'hurrah', 'meh', 'mhm', 'muahaha', 'nuh-uh', 'oh', 'ooh', 'ooh-la-la', 'oomph', 'oops', 'ouch', 'oww', 'oy', 'pew', 'pff', 'phew', 'psst', 'sheesh', 'shh', 'shoo', 'tsk-tsk', 'uh-hu', 'uh-oh', 'uh-uh', 'uhh', 'um', 'umm', 'wee', 'well', 'whoa', 'wow', 'yahoo', 'yay', 'yeah', 'yikes', 'yippee', 'yoo-hoo', 'yuck', 'yuh-uh', 'zing',
  368. //modals
  369. 'can', 'could', 'be able to', 'may', 'might', 'shall', 'should', 'must', 'have to', 'will', 'would',
  370. ].sort((a, b) => b.length - a.length).map(w => `\\W${w}(?!\\w)`).join('|'), 'g')), ' ');
  371. }
  372.  
  373. /**
  374. * @param {string} q
  375. * @param {Date} [before]
  376. * @param {Date} [after]
  377. * @param {string[]} [tags]
  378. */
  379. async function findByApi(q, before, after, tags) {
  380. var dfgdr = q && fetch(
  381. `https://api.stackexchange.com/2.2/search?page=1&pagesize=1&order=desc&sort=relevance&intitle=${encodeURIComponent(q)}&site=stackoverflow` +
  382. (after ? '&fromdate=' + (after.getTime() / 1000 - 120 | 0) : '') +
  383. (before ? '&todate=' + (before.getTime() / 1000 + 120 | 0) : '') +
  384. (Array.isArray(tags) && tags.length > 0 ? '&tagged=' + encodeURIComponent(Array.from(new Set(tags)).join(';')) : ''), {
  385. credentials: 'omit'
  386. })
  387. .then(r => r.json())
  388. .then(r => r?.items[0]?.link);
  389. return dfgdr;
  390. }
  391.  
  392. /**
  393. * @param {string|array} [h] - header selector (def: 'h1')
  394. * @param {string|array} [t] - tags selector (def: '.tag')
  395. * @param {string} [l] - lang (def: none)
  396. * @param {string} [s] - target site(s) (def: ['stackoverflow.com'])
  397. */
  398. async function byHeader(h, t, l, s) {
  399. var sbh = filterText((l == 'en') ? textContent(h) : await yaTranslate(getHeader(h), l), 1);
  400. return sbh && (await findByApi(sbh, _, _, getTags(t)) || prepareSearch(sbh, t, s));
  401. }
  402.  
  403. async function byPath(pos, s) {
  404. var fbp = _ps[pos].replace(/[-+ ]/g, ' ').replace(/(-closed|-duplicate)?(\.html)?$/, '');
  405. return (await findByApi(fbp)) || prepareSearch(fbp, '', s);
  406. }
  407.  
  408. function startsByText(selector, text, href = false) {
  409. const e = document.querySelectorAll(selector);
  410. for (var i = 0; i < e.length; i++) {
  411. var t = e[i].innerText.trim();
  412. var f = t.indexOf(text);
  413. if (f == 0) return (href ? e[i].querySelector(href)?.href : t.substr(text.length).trim());
  414. }
  415. }
  416.  
  417. function byInner(selector, text) {
  418. const e = document.querySelectorAll(selector);
  419. for (var i = 0; i < e.length; i++) {
  420. if (e[i].innerText.trim().indexOf(text) >= 0) return e[i].href;
  421. }
  422. }
  423.  
  424. var link;
  425. const host = location.hostname.split('.').slice(-2).join('.');
  426. console.log('Checking site: ' + location.hostname + ' as ' + host);
  427.  
  428. switch (host) {
  429. case '1r1g.com':
  430. return clr('#343a40') && byHeader();
  431. case '8101010108.cn':
  432. case 'desarrollo-web-br-bd.com':
  433. case 'pengembangan-web-mp-pd.com':
  434. case 'reponse-question-developpement-web-bd.com':
  435. case 'risposta-alla-domanda-sullo-sviluppo-web-bd.com':
  436. case 'tra-loi-cau-hoi-phat-trien-web.com':
  437. case 'web-dev-qa-db-fr.com':
  438. case 'web-dev-qa-db-ja.com':
  439. case 'web-gaebal-jilmun-dabbyeon-db.com':
  440. case 'web-gelistirme-sc.com':
  441. return bySel('.q-source>a');
  442. case 'answacode.com':
  443. case 'bestecode.com':
  444. case 'bonprog.com':
  445. case 'coderquestion.ru':
  446. case 'coredump.biz':
  447. case 'gitrush.ru':
  448. case 'html-agility-pack.net':
  449. case 'issue.life':
  450. case 'profikoder.com':
  451. case 'progaide.com':
  452. case 'progexact.com':
  453. case 'programqa.com':
  454. case 'qaru.tech':
  455. case 'thinbug.com':
  456. case 'xbuba.com': // site offline / site not found / 2022-05-01
  457. return byNumber(_ps[2]);
  458. case 'antwortenhier.me':
  459. lng('de');
  460. case 'askfrance.me':
  461. lng('fr');
  462. case 'respuestas.me':
  463. lng('es');
  464. case 'askentire.net':
  465. clr('#2c3e50') && lng('ru');
  466. return byHeader('h1', [await transTags('ul.x-tags li a[href*="/t/"]')], lang);
  467. case 'askdev.ru':
  468. return clr('#970f1b') && urlByImg('https://superuser.com/questions/') || byHeader('h1', [await transTags('.block_taxonomies a')], 'ru');
  469. case 'askubuntu.ru':
  470. return byHeader('h1', 'nav .col-tag', 'ru', ['askubuntu.com']);
  471. case 'askvoprosy.com':
  472. return byPath(2);
  473. case 'bcqaw.com':
  474. return byHeader('h1.article-title', _, 'zh');
  475. case 'bildiredi.com':
  476. case 'ciupacabra.com':
  477. case 'de-vraag.com':
  478. case 'donolik.com':
  479. case 'kotaeta.com':
  480. case 'pytannie.com':
  481. case 'sozdizimi.com':
  482. case 'switch-case.com':
  483. case 'while-do.com':
  484. case 'zapytay.com':
  485. return bySel('a.link.block');
  486. case 'bilee.com':
  487. clr('#178acc');
  488. case 'techarks.ru':
  489. clr('#20a169');
  490. case 'quares.ru': // site offline / site not found / 2022-05-01
  491. clr('#fcdb00');
  492. case 'question-it.com':
  493. clr('#2c3e50');
  494. case 'legkovopros.ru':
  495. return clr('#55b252') && byHeader('h1', '.tag', 'ru');
  496. case 'code-examples.net':
  497. case 'ffff65535.com':
  498. case 'i-harness.com':
  499. case 'src-bin.com':
  500. return byNumber(lastPathPart(), 16);
  501. case 'codegrepper.com':
  502. return _go(bySel('.answer_source>a'));
  503. case 'codeindex.ru': // site offline / Cloudflare error / 2022-05-01
  504. case 'qa-help.ru': // site offline / Cloudflare error / 2022-05-01
  505. return bySel('span.text-muted.fake_url', 'src') || _tc('.text-muted.small');
  506. case 'codengineering.ru':
  507. return toSearch(lastPathPart().replace(/(-closed|-duplicate)?(-\d+)?(\.html)?$/, ''), true);
  508. case 'codenong.com':
  509. case 'coderoad.ru':
  510. case 'coderoad.wiki': // site offline / Cloudflare error / 2022-05-01
  511. case 'quabr.com':
  512. return byNumber(_ps[1]);
  513. case 'coder.work':
  514. return bySel('div>p>a[rel="noreferrer noopener nofollow"]') || startsByText('p', 'stackoverflow链接', 'a[href*="stackoverflow.com"]') || startsByText('p', 'stackoverflow原址', 'a[href*="stackoverflow.com"]') || byHeader('h1', _/*'div[style="width: 100%;"] a[href*="/blog?tag="]'*/, 'zh');
  515. case 'coderedirect.com': // site offline / timeout / 2022-05-04
  516. case 'fullstackuser.com':
  517. return byHeader('h1', '.custom-head .post-tag', 'en');
  518. case 'coderoad.in':
  519. return byPath(3);
  520. case 'codersatellite.com': // site offline / timeout / 2022-05-01
  521. return byNumber(_ps[1].split('-')[3]);
  522. case 'developreference.com':
  523. var parts = document.title.split(' - ');
  524. var devpref = _ps[3].replace(/[-+ ]/g, ' ').replace(/(%ef|%bc|%9f)+$/i, '');
  525. return (await findByApi(devpref)) || (await findByApi(parts.join(' - '), _, _, [parts.pop()])) || promtRedirect(sitecolor, toSearch(devpref));
  526. case 'devfaq.fr':
  527. return byHeader('h1', '.badge-info', 'fr');
  528. case 'e-learn.cn':
  529. return startsByText('div.content p:last-child', '来源:');
  530. case 'exceptionshub.com': // site offline / Cloudflare error / 2022-05-01
  531. return _c(/\.html$/) && byPath(1);
  532. case 'extutorial.com':
  533. return byHeader('h1', 'a[href*="/tags/"]', 'en');
  534. case 'fixes.pub':
  535. return byHeader('h1', 'aside li a[href*="fixes.pub/topics"]', 'ja');
  536. case 'fluffyfables.com':
  537. return _c(/^\/([0-9]+)([a-z\-]+)$/) && clr('#2c3e50') && (badCode = true) && byHeader('h1', 0, 'nl');
  538. case 'icode9.com':
  539. return _go(textContent('#paragraph > p:last-child').split('来源:', 2)[1].trim());
  540. case 'itdaan.com':
  541. return _go(bySel('input[name="url"]', 'value'));
  542. case 'javaer101.com':
  543. lng(_ps[1] == "article"?'ja':_ps[1]);
  544. return byHeader('h1', 'nav .col-tag', lang);
  545. case 'kompsekret.ru':
  546. return clr('#292d2f') && (urlByImg('https://superuser.com/questions/') || byHeader([lastPathPart().replace(/(-closed|-duplicate)?(\d+)?(\.html)?$/, '').replace(/-/g, ' ')], '.tags a', 'en', ['superuser.com']));
  547. case 'localcoder.org':
  548. return byHeader('h1', '.categories a', 'en');
  549. case 'mlink.in':
  550. case 'sqlite.in':
  551. if(!_t('h1 a')) return;
  552. badImgs = true;
  553. return bySel('a[rel="nofollow"][target="_blank"]') || byHeader([_t('.qa-main-heading h1').innerText.replace(/^(\s+)?([a-z])+\s-/, '').trim()], '.qa-q-view-main .qa-tag-link', 'en', '');
  554. case 'mlog.club':
  555. addJS('var redir = window.__NUXT__.data[0].article.sourceUrl; redir && window.location.replace(redir);');
  556. return lng('zh') && byHeader('h1', [await transTags('.article-tag')], 'zh');
  557. case 'newbedev.com':
  558. return _t('article') && byHeader('h1', 'h4.tags a.item-tag', 'en', ['superuser.com', 'serverfault.com', 'stackoverflow.com', 'stackexchange.com']);
  559. case 'poweruser.guru':
  560. return _t('div.post-menu a.suggest-edit-post[href*="superuser.com/questions/"]');
  561. case 'progi.pro': //.question-type .author a
  562. return clr('#4e82c2') && byHeader('h1[itemprop="name"]', '.tag-list a', 'ru');
  563. case 'proubuntu.ru':
  564. return byHeader('h1>a>span[itemprop="name"]', [await transTags('a[rel="tag"]')],'ru', ['askubuntu.com']);
  565. case 'recalll.co': // site offline / site not found / 2022-05-01
  566. return _t('div.label-wrap a[href*="stackoverflow.com/"][target="_blank"]')?.href || byHeader('h2#mainTitle', 'a[href*="/tags/"]', 'en');
  567. case 'ruphp.com':
  568. return byHeader('h1', '.breadcrumb-item .badge a', 'ru');
  569. case 'sobrelinux.info':
  570. return byHeader('h1', '.tags .tag a', 'pt', ['superuser.com', 'serverfault.com', 'stackoverflow.com', 'stackexchange.com']);
  571. case 'soinside.com':
  572. return clr('#333') && byHeader('h1', '.q-tag', 'zh');
  573. case 'stackanswers.net':
  574. clr('#999') && lng('en');
  575. return location.hostname.startsWith('publish.') && all('.panel-body a')[1].href;
  576. case 'stackoom.com':
  577. return byNumber(document.getElementById('question').dataset.questionid);
  578. case 'stackoverflood.com':
  579. return (tt = _h.match(/^https?:\/\/stackoverflood\.com\/([a-zA-Z]{2})\/q\/(.+)/)) && byNumber(tt[2]);
  580. case 'stormcrow.dev':
  581. return byNumber(_ps[3]);
  582. case 'techfeed.net':
  583. return byHeader('main h1', '.tag', 'ru');
  584. case 'tencent.com':
  585. return byHeader('.ask-title h2', _, 'zh');
  586. case 'utyatnishna.ru':
  587. return byHeader('h1.entry-title', '.tag', 'ru');
  588. case 'v-resheno.ru':
  589. return textContent('.linkurl > b');
  590. case 'wikiroot.ru':
  591. tt = _t('section section div.footer-post div.d-inline-block button');
  592. tt = tt && (getAttr(tt, 'data-url', /https?:\/\/wikiroot\.ru\/comment\/new\/([0-9]+)/) || getAttr(tt, 'data-target', /#buttoncollapse-([0-9]+)/));
  593. return tt ? 'https://superuser.com/questions/' + tt : byHeader('h1', 'ul.tags-list li a', 'ru');
  594. case 'xiu2.net':
  595. addJS('var redir = window.__NUXT__.data[0].info.sourceUrl; redir && window.location.replace(redir);');
  596. return lng('zh') && byHeader('h1', '.contents .tag-time a[href*="/it/tag/"]', 'zh');
  597. case 'xszz.org':
  598. return clr('#ff6f06') && byHeader('.post-h1title h1', 0, 'en');
  599. case 'ylhow.com':
  600. return (tt = _t('.entry-content > p > a[href*="stackoverflow.com/"]')) && tt.innerText.includes('原文') && tt.href;
  601. case 'yuanmacha.com':
  602. return (ll = _t('h1')?.innerHTML.match(/\((.+)\)/)) && byHeader([ll[1]], '.tag a', 'en', ['stackoverflow.com']);
  603. /* Wikipedia */
  604. case '360wiki.ru':
  605. case 'buildwiki.ru':
  606. case 'datewiki.ru':
  607. case 'hmong.ru':
  608. case 'hmong.wiki':
  609. case 'livepcwiki.ru':
  610. case 'vvikipedla.com':
  611. case 'wiki2.wiki':
  612. case 'wikichi.ru':
  613. case 'wikivisually.com':
  614. return wiki('en', 2);
  615. case 'abcdef.wiki':
  616. return wiki('en', _p, false);
  617. case 'encyclopaedia.bid':
  618. return wiki('ru', _p.replace(/^\/%D0%B2%D0%B8%D0%BA%D0%B8%D0%BF%D0%B5%D0%B4%D0%B8%D1%8F/, '/wiki'), false);
  619. case 'encyclopedia.kz':
  620. return _hst('ru.encyclopedia.kz') && wiki('ru', 2);
  621. case 'gaz.wiki':
  622. return wiki('en', 3);
  623. case 'jejakjabar.com': // all pages 404 / 2022-05-01
  624. return (tt = _h.match(/https?:\/\/([a-zA-z]+\.)?jejakjabar\.com\/wiki\/(.+)/)) && wiki('en', tt[2]);
  625. case 'mihalicdictionary.org': // site offline / site not found / 2022-05-01
  626. return (tt = _h.match(/https?:\/\/([a-zA-Z]{2})?\.?mihalicdictionary\.org(.+)/)) && wiki(tt[1], tt[2], false);
  627. case 'nina.az':
  628. return (tt = _h.match(/https?:\/\/wikipedia\.(([a-z]{2})\.)?nina\.az\/wiki\/(.+)/)) && wiki(mulreplace(tt[2], [ ['ua', 'uk'], ['us', 'en'] ]), tt[3]);
  629. case 'rudata.ru':
  630. return bySel('a.external[href*="ru.wikipedia.org"]');
  631. case 'sbup.com':
  632. case 'wiki-org.ru':
  633. case 'wikiredia.ru':
  634. return wiki('ru', _p, false);
  635. case 'territorioscuola.it':
  636. return (tt = _h.match(/https?:\/\/enhancedwiki\.territorioscuola\.it\/\?title=(.+)/)) && wiki('it', tt[1]);
  637. case 'wiki-wiki.ru':
  638. return wiki('ru', 3);
  639. case 'wiki.cologne':
  640. case 'wiki2.info':
  641. case 'wiki2.online': // other content / 2022-05-01
  642. case 'wikipedia24.ru':
  643. return _t('.mw-parser-output') && wiki('ru', 1);
  644. case 'wiki2.net':
  645. case 'wikipedia.tel':
  646. return 'https://ru.wikipedia.org/wiki' + _p;
  647. case 'wiki2.org':
  648. if (/\?search=/.test(location.search)) return;
  649. return (tt = _h.match(/https?:\/\/(([a-z]{2})\.)?wiki2\.org\/([a-zA-z]{2})\/(.+)/)) && wiki(tt[3], '/wiki/' + tt[4], false);
  650. case 'wikidark.ru':
  651. return _go('https://ru.wikipedia.org' + _p + location.search);
  652. case 'wikies.wiki':
  653. case 'wikipe.wiki':
  654. return wiki(2, 3);
  655. case 'wikipedia-on-ipfs.org':
  656. return wiki(_hp(3), 2);
  657. case 'wikiwand.com':
  658. return !(/(\?|&)fullSearch=(true|false)/.test(location.search)) && wiki(1, 2);
  659. case 'wikizero.com':
  660. return wiki(1, 2);
  661. case 'xcv.wiki':
  662. return (tt = _h.match(/https?:\/\/([a-zA-z]{2,4})\.xcv\.wiki\/wiki\/(.+)/)) && wiki('de', tt[2]);
  663. /* GitHub */
  664. case 'codefactor.io':
  665. document.addEventListener("DOMContentLoaded", (e)=>{
  666. if (_ps[2]=='github' && _ps[5]=='source') return _go(bySel('a[title^="View on"]') + '/blob/' + _ps.splice(6).join('/'));
  667. return _go(bySel('a.page-title-link') || bySel('a[analytics-event^="View file on"]') || bySel('a[title^="View on"]'));
  668. });
  669. break;
  670. case 'giters.com':
  671. return _go('https://github.com' + _p);
  672. case 'githubhot.com': // site offline / site not found / 2022-05-01
  673. case 'githubmemory.com':
  674. return _c(/^\/(repo\/|@)/) && _go('https://github.com' + _p.replace(/^\/(repo\/|@)/,'/'));
  675. case 'githublab.com':
  676. return _go('https://github.com' + _p.replace(/^\/(repository|profile)/,'').replace(/^(\/issues)(\/.*\/.*)(\/.*)/,"$2$1$3").replace(/^(\/issues)(\/.*\/.*)/,"$2$1"));
  677. case 'higithub.com':
  678. return _go('https://github.com' + _p.replace(/\/(repo\/|user$)/,'/').replace(/^(\/.*)(\/issue)(\/.*)(\/.*)/,"$1$3$2s$4").replace(/^(\/.*)\/repo_(issues)(\/.*)/,"$1$3/$2"));
  679. case 'issueantenna.com':
  680. return _go('https://github.com' + _p.replace(/^\/(repo|author)/,''));
  681. case 'issueexplorer.com': // other content / 2022-05-01
  682. return _go('https://github.com' + _p.replace(/^\/repo/,''));
  683. case 'jsrepos.com':
  684. return _go(bySel('article.markdown-body>a[rel="nofollow"]:last-child'));
  685. case 'lifesaver.codes':
  686. return byInner('a[role="link"]','Original');
  687. /* Other */
  688. case 'dir.md':
  689. return (tt = _h.match(/^https?:\/\/dir.md\/(.+)(&|\?)host=([a-zA-Z\.-]+)$/)) && _go('https://' + tt[3] + '/' + tt[1]);
  690. case 'it-brain.online':
  691. return 'https://tutorialspoint.com/' + _ps[2];
  692. case 'itnan.ru':
  693. return _h.match(/https?:\/\/([a-zA-Z]{2})?\.?itnan\.ru\/post\.php\?(.+)?p=([0-9]+)/) && bySel('article.entry .entry-meta a[title="Оригинальная публикация"]');
  694. case 'npmmirror.com':
  695. return _go('https://www.npmjs.com'+_p);
  696. case 'savepearlharbor.com':
  697. return bySel('article.post > div.entry-content > p > a[href*="://habr.com/"]');
  698. case 'snyk.io':
  699. return _go('https://www.npmjs.com/'+_ps[3]);
  700. default:
  701. if (_hst('webentwicklung-frage-antwort-db.com.de')) {
  702. return bySel('.q-source>a');
  703. } else if (_hst('qastack') || _hst('qa-stack')) {
  704. return bySel('span.text-muted.fake_url a, span.text-muted.fake_url', 'src') ||
  705. bySel('.text-muted a:last-child[href*="stackoverflow.com/"],.text-muted a:last-child[href*="stackexchange.com/"],.text-muted a:last-child[href*="serverfault.com/"],.text-muted a:last-child[href*="superuser.com/"],.text-muted a:last-child[href*="mathoverflow.net/"]') ||
  706. ((tt = _h.match(/https?:\/\/qa-?stack\.([a-z\.]+)\/([a-z]+)\/([0-9]+)\/(.+)/)) && 'https://' + tt[2] + '.stackexchange.com/questions/' + tt[3] + '/' + tt[4]);
  707. } else {
  708. console.log('check by selectors');
  709. const cssSelectors = {
  710. '4answered.com': '.view_body span a',
  711. 'answer-id.com': 'a.link', // all pages 404 / 2022-05-01
  712. 'answeright.com': 'a.link',
  713. 'ask-ubuntu.ru': '.q-source',
  714. 'askdev.info': '.question-text > .a-link', // site offline / site not found / 2022-05-01
  715. 'catwolf.org': '.text-left.small>a',
  716. 'codegear.dev': 'p.text-right>a',
  717. 'e-learn.cn': '.zhuanzai + div a',
  718. 'fooobar.com': '.question-text > .aa-link', // all pages 404 / 2022-05-01
  719. 'generacodice.com': '#fontePrincipale > a.link',
  720. 'howtosolves.com': '#question .question .source a',
  721. 'husl.ru': '.source-link',
  722. 'itranslater.com': '.body > div:last-child > a',
  723. 'overcoder.net': '.info_outlink',
  724. 'overcoder.ru': '.info_outlink',
  725. 'prog-help.ru': '.eclip > a',
  726. 'programmerz.ru': '.source-share-link',
  727. 'py4u.net': '.question .author .src a', // site offline / site not found / 2022-05-01
  728. 'pythonq.com':'a[style="color:red"]', // site offline / site not found / 2022-05-01
  729. 'qacode.ru': '.question-info .cc-link', // all pages 404 / 2022-05-01
  730. 'qarchive.ru': 'cite > a',
  731. 'qarus.ru': 'em > a',
  732. 'qna.one': '.page-container-question .source-share-block a',
  733. 'rstopup.com': '.td-post-content .origlink > a',
  734. 'serveanswer.com':'a[title="Source"]',
  735. 'sprosi.pro': '#qsource > a',
  736. 'stackru.com': '.q-source',
  737. 'try2explore.com': 'div.tagsandsource span.source a[target="_blank"]', // site offline / site not found / 2022-05-01
  738. 'ubuntugeeks.com': '.question-text > .a-link', // site offline / site not found / 2022-05-01
  739. 'uwenku.com': '.post-info a', // site offline / site not found / 2022-05-01
  740. 'voidcc.com': '.source > a',
  741. 'web-answers.ru': '.source > a',
  742. };
  743. link = cssSelectors[host] && _tc(cssSelectors[host]);
  744. console.log(link);
  745. }
  746. }
  747. return link;
  748. })().then(link => {
  749. var sredir = (r) => r && fetch(`https://api.zcxv.icu/b2s.php?q=set&url=${encodeURIComponent(location.href)}&redir=${encodeURIComponent(r)}`, { method: 'GET', credentials: 'omit' });
  750.  
  751. function cbufw(u, s) {
  752. var count = 10;
  753. var pos = GM_getValue('b2s-pos') || 0;
  754. var db = JSON.parse(GM_getValue('b2s') || '{}');
  755. pos = pos > count ? 0 : ++pos;
  756. GM_setValue('b2s-pos', pos);
  757. db[pos] = [u, s];
  758. GM_setValue('b2s', JSON.stringify(db));
  759. }
  760.  
  761. function run(u, s) {
  762. console.log('Redirect link: ' + u) || sredir(u);
  763. cbufw(location.href, u) || window.location.replace(u);
  764. }
  765.  
  766. var fix = (a, b, s = 0) => (link.match(a)) ? (run(link.replace(a, b), s) || true) : false;
  767.  
  768. link = link?.href ?? link;
  769. console.log('Result link: ' + link);
  770. if (!link || typeof link !== 'string') return;
  771. if (link.includes('wikipedia.org/wiki/wiki')) link = link.replace(/wikipedia\.org\/wiki\/wiki/, 'wikipedia.org/wiki');
  772.  
  773. //valid links
  774. if (/^https?:\/\/((pt|ja|ru|es)\.)?stackoverflow\.com\/questions\/([0-9]{1,12})/.test(link) ||
  775. /^https?:\/\/(([a-zA-z\-]+\.)?)stackexchange\.com\/questions\/([0-9]{1,12})/.test(link) ||
  776. /^https?:\/\/(superuser\.com|askubuntu\.com|mathoverflow\.net|serverfault.com)\/questions\/([0-9]{1,12})/.test(link) ||
  777. /^https?:\/\/(([a-zA-z\-]+\.)?)(habr\.com|geektimes\.ru)\/(.+)/.test(link)) {
  778. return run(link, 1);
  779. }
  780. if (/^https?:\/\/(([a-zA-z\-]+\.)?)wikipedia\.org\/wiki\/(.+)/.test(link) ||
  781. /^https?:\/\/github\.com\/(.+)?/.test(link) ||
  782. /^https?:\/\/tutorialspoint\.com\/(.+)/.test(link)) {
  783. return run(link);
  784. }
  785. fix(/^https?:\/\/((pt|ja|ru|es)\.)?stackoverflow\.com\/([a-z]+)\/([0-9]{1,12})/, 'https://$1stackoverflow.com/questions/$4', 1) ||
  786. fix(/^https?:\/\/([a-z]+\.)?stackexchange\.com\/q\/([0-9]{1,12})/, 'https://$1stackexchange.com/questions/$2', 1) ||
  787. fix(/^https?:\/\/([a-z]+\.)?stackexchange\.com\/([a-z]+)\/([0-9]{1,12})/, 'https://$2.stackexchange.com/questions/$3', 1) ||
  788. fix(/^https?:\/\/([a-z]+\.)?(superuser\.com|askubuntu\.com|mathoverflow\.net|serverfault.com)\/([a-z]+)\/([0-9]{1,12})/, 'https://$1$2/questions/$4', 1) ||
  789. fix(/^https?:\/\/([a-z]+\.)?wikipedia\.org\/w\/index\.php\?title=(.+)&oldid=([0-9]{1,12})/, 'https://$1wikipedia.org/wiki/$2');
  790.  
  791. }).catch(console.error.bind(console));