Back2source

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

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

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