Back2source

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

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

  1. // ==UserScript==
  2. // @name Back2source
  3. // @version 0.1.89
  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 *://npmmirror.com/package/*
  206. // @match *://qa.1r1g.com/sf/ask/*
  207. // @match *://respuestas.me/*
  208. // @match *://ru.encyclopedia.kz/index.php/*
  209. // @match *://snyk.io/advisor/npm-package/*
  210. // @match *://proubuntu.ru/*/*
  211. // @match *://pythonq.com/*/*/*
  212. // ==/UserScript==
  213. (async () => {
  214. 'use strict';
  215.  
  216. var sitecolor = '#333';
  217. var lang = 'ru';
  218. var badCode = false;
  219. var badImgs = false;
  220. var _ = undefined;
  221. var ll, tt;
  222. const _p = location.pathname;
  223. const _ps = _p.split('/');
  224. const _h = location.href;
  225. var _$s = (s) => typeof s === 'string' || s instanceof String; //is_string
  226. var _hp = (p=3) => (ll = location.hostname.split('.')) && ll[ll.length-p];//get_subdomain (host part)
  227. var _t = (s) => document.querySelector(s);
  228. var _c = (r) => r.test(_p);
  229. var _go = (s) => s && window.location.replace(s);
  230. var _hst = (s) => location.hostname.includes(s);
  231. var clr = (c, f) => (sitecolor = (f || sitecolor == '#333') ? c : sitecolor);
  232. var lng = (c, f) => (lang = (f || lang == 'ru') ? c : lang);
  233. var lastPathPart = () => _ps.filter(Boolean).slice(-1)[0];
  234. var bySel = (s, a = 'href') => _t(s)?.getAttribute(a);
  235. var getHeader = (h) => removeAuxiliary(h ? (Array.isArray(h) ? h[0] : textContent(h)) : textContent('h1'));
  236. var getTags = (t) => t ? (Array.isArray(t) ? t[0] : allTexts(t)) : allTexts('.tag');
  237. var mulreplace = (str, a) => a.forEach((v) => (str = str.replace(v[0], v[1]))) || str;
  238. var wiki = (l = 0, p = 2, w = true) => 'https://' + (_$s(l) ? l : _ps[l]) + '.wikipedia.org' + (w ? '/wiki/' : '') + (_$s(p) ? p : _ps[p]);
  239. 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')])]);
  240. var transTags = async (t) => (await yaTranslate(allTexts(t).join(' '), lang)).split(' ');
  241. 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);
  242. var textContent = (selector) => _t(selector)?.textContent.trim();
  243. var byNumber = (s, radix) => (s = parseInt(s, radix)) && s > 0 ? _go('https://stackoverflow.com/questions/' + s) : null;
  244. var normalize = (s) => s && ' ' + s.toLowerCase() + ' ';
  245. var pipe = (...fns) => x => fns.reduce((v, f) => f(v), x);
  246. var all = (s) => Array.prototype.slice.call(document.querySelectorAll(s));
  247. var allTexts = (s) => all(s).map(a => a.textContent.trim());
  248. var allAttr = (s, t) => all(s).map(a => a[t].trim());
  249. var getAttr = (t, a, r, s = '$1') => (t.hasAttribute(a)) && t.getAttribute(a).replace(r, s);
  250. var dropMarks = (s) => s && s.replace(/\[(на удержании|on hold|duplikować|duplicado|duplicar|duplikat|dublicate|duplicate|дубликат|закрыто|закрытый|closed|geschlossen|zamknięte|cerrado|重复|repeat)\]\s*$/i, '').trim();
  251. var fromBrackets = (h = 'h1', t, l, s) => (ll = _t(h)?.innerHTML.match(/\(([a-zA-Z-_ ])+\)/)) && byHeader([ll[0]], t, l, s);
  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') ? getHeader(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 'wikiroot.ru':
  448. tt = _t('section section div.footer-post div.d-inline-block button');
  449. tt = tt && (getAttr(tt, 'data-url', /https?:\/\/wikiroot\.ru\/comment\/new\/([0-9]+)/) || getAttr(tt, 'data-target', /#buttoncollapse-([0-9]+)/));
  450. return tt ? 'https://superuser.com/questions/' + tt : byHeader('h1', 'ul.tags-list li a', 'ru');
  451. case 'newbedev.com':
  452. return _t('article') && byHeader('h1', 'h4.tags a.item-tag', 'en', ['superuser.com', 'serverfault.com', 'stackoverflow.com', 'stackexchange.com']);
  453. case 'sobrelinux.info':
  454. return byHeader('h1', '.tags .tag a', 'pt', ['superuser.com', 'serverfault.com', 'stackoverflow.com', 'stackexchange.com']);
  455. case 'ruphp.com':
  456. return byHeader('h1', '.breadcrumb-item .badge a', 'ru');
  457. case 'yuanmacha.com':
  458. return fromBrackets('h1', '.tag a', 'en');
  459. case 'stormcrow.dev':
  460. return byNumber(_ps[3]);
  461. case 'stackoom.com':
  462. return byNumber(document.getElementById('question').dataset.questionid);
  463. case 'ffff65535.com':
  464. case 'src-bin.com':
  465. case 'i-harness.com':
  466. case 'code-examples.net':
  467. return byNumber(lastPathPart(), 16);
  468. case 'coderedirect.com':
  469. return byHeader('h1', '.custom-head .post-tag', 'en');
  470. case 'coderoad.ru':
  471. case 'coderoad.wiki':
  472. case 'codenong.com':
  473. case 'quabr.com':
  474. return byNumber(_ps[1]);
  475. case 'answacode.com':
  476. case 'bestecode.com':
  477. case 'bonprog.com':
  478. case 'coderquestion.ru':
  479. case 'coredump.biz':
  480. case 'gitrush.ru':
  481. case 'html-agility-pack.net':
  482. case 'issue.life':
  483. case 'profikoder.com':
  484. case 'progaide.com':
  485. case 'progexact.com':
  486. case 'programqa.com':
  487. case 'qaru.tech':
  488. case 'thinbug.com':
  489. case 'xbuba.com':
  490. return byNumber(_ps[2]);
  491. case 'proubuntu.ru':
  492. return byHeader('h1>a>span[itemprop="name"]', [await transTags('a[rel="tag"]')],'ru', ['askubuntu.com']);
  493. case 'javaer101.com':
  494. return byHeader('h1', 'nav .col-tag');
  495. case 'fixes.pub':
  496. return byHeader('h1', 'aside li a[href*="fixes.pub/topics"]', 'ja');
  497. case 'askubuntu.ru': //#Question div.question-text span[itemprop="author"] span[itemprop="name"]
  498. return byHeader('h1', 'nav .col-tag', 'ru', ['askubuntu.com']);
  499. case 'devfaq.fr':
  500. return byHeader('h1', '.badge-info', 'fr');
  501. case 'askfrance.me':
  502. lng('fr');
  503. case 'respuestas.me':
  504. lng('es');
  505. case 'antwortenhier.me':
  506. lng('de');
  507. case 'askentire.net':
  508. clr('#2c3e50') && lng('ru');
  509. return byHeader('h1', [await transTags('ul.x-tags li a[href*="/t/"]')], lang);
  510. case 'xiu2.net':
  511. addJS('var redir = window.__NUXT__.data[0].info.sourceUrl; redir && window.location.replace(redir);');
  512. return lng('zh') && byHeader('h1', '.contents .tag-time a[href*="/it/tag/"]', 'zh');
  513. case 'mlog.club':
  514. addJS('var redir = window.__NUXT__.data[0].article.sourceUrl; redir && window.location.replace(redir);');
  515. return lng('zh') && byHeader('h1', [await transTags('.article-tag')], 'zh');
  516. case 'bilee.com':
  517. clr('#178acc');
  518. case 'question-it.com':
  519. clr('#2c3e50');
  520. case 'quares.ru':
  521. clr('#fcdb00');
  522. case 'techarks.ru':
  523. clr('#20a169');
  524. case 'legkovopros.ru':
  525. return clr('#55b252') && byHeader('h1', '.tag', 'ru');
  526. case 'tencent.com':
  527. return byHeader('.ask-title h2', _, 'zh');
  528. case 'bcqaw.com':
  529. return byHeader('h1.article-title', _, 'zh');
  530. case 'techfeed.net':
  531. return byHeader('main h1', '.tag', 'ru');
  532. case 'utyatnishna.ru':
  533. return byHeader('h1.entry-title', '.tag', 'ru');
  534. case 'fluffyfables.com':
  535. return _c(/^\/([0-9]+)([a-z\-]+)$/) && clr('#2c3e50') && (badCode = true) && byHeader('h1', 0, 'nl');
  536. case 'coderoad.in':
  537. return byPath(3);
  538. case 'exceptionshub.com':
  539. return _c(/\.html$/) && byPath(1);
  540. case 'recalll.co':
  541. return _t('div.label-wrap a[href*="stackoverflow.com/"][target="_blank"]')?.href || byHeader('h2#mainTitle', 'a[href*="/tags/"]', 'en');
  542. case 'coder.work':
  543. 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');
  544. case 'extutorial.com':
  545. return byHeader('h1', 'a[href*="/tags/"]', 'en');
  546. case '1r1g.com':
  547. return clr('#343a40') && byHeader();
  548. case 'soinside.com':
  549. return clr('#333') && byHeader('h1', '.q-tag', 'zh');
  550. case 'xszz.org':
  551. return clr('#ff6f06') && byHeader('.post-h1title h1', 0, 'en');
  552. case 'progi.pro': //.question-type .author a
  553. return clr('#4e82c2') && byHeader('h1[itemprop="name"]', '.tag-list a', 'ru');
  554. case 'developreference.com': // https://html.developreference.com/article/23259983/How+extension+get+the+text+selected+in+chrome+pdf+viewer%EF%BC%9F
  555. var parts = document.title.split(' - ');
  556. var devpref = _ps[3].replace(/[-+ ]/g, ' ').replace(/(%ef|%bc|%9f)+$/i, '');
  557. return (await findByApi(devpref)) || (await findByApi(parts.join(' - '), _, _, [parts.pop()])) || promtRedirect(sitecolor, toSearch(devpref));
  558. case 'intellipaat.com': //Cloudflare Error 1020 (03.10.21)
  559. return byHeader('h1', '.qa-q-view-main .qa-tag-link', 'en', '');
  560. case 'ylhow.com': //Cert expired 03.09.21 (02.10.21)
  561. return (tt = _t('.entry-content > p > a[href*="stackoverflow.com/"]')) && tt.innerText.includes('原文') && tt.href;
  562. case 'e-learn.cn':
  563. return startsByText('div.content p:last-child', '来源:');
  564. case 'icode9.com':
  565. return _go(textContent('#paragraph > p:last-child').split('来源:', 2)[1].trim());
  566. case 'v-resheno.ru':
  567. return textContent('.linkurl > b');
  568. case 'poweruser.guru':
  569. return _t('div.post-menu a.suggest-edit-post[href*="superuser.com/questions/"]');
  570. case 'stackanswers.net':
  571. clr('#999') && lng('en');
  572. case 'askvoprosy.com':
  573. return byPath(2);
  574. case 'codeday.me': // ads or deleted (03.10.21)
  575. return location.hostname.startsWith('publish.') && all('.panel-body a')[1].href;
  576. case 'codengineering.ru':
  577. return toSearch(lastPathPart().replace(/(-closed|-duplicate)?(-\d+)?(\.html)?$/, ''), true);
  578. case 'askdev.ru':
  579. return clr('#970f1b') && urlByImg('https://superuser.com/questions/') || byHeader('h1', [await transTags('.block_taxonomies a')], 'ru');
  580. case 'kompsekret.ru':
  581. return clr('#292d2f') && (urlByImg('https://superuser.com/questions/') || byHeader([lastPathPart().replace(/(-closed|-duplicate)?(\d+)?(\.html)?$/, '').replace(/-/g, ' ')], '.tags a', 'en', ['superuser.com']));
  582. case 'itdaan.com':
  583. return _go(bySel('input[name="url"]', 'value'));
  584. case 'stackoverflood.com':
  585. return (tt = _h.match(/^https?:\/\/stackoverflood\.com\/([a-zA-Z]{2})\/q\/(.+)/)) && byNumber(tt[2]);
  586. case 'it-brain.online':
  587. return 'https://tutorialspoint.com/' + _ps[2];
  588. case 'wikidark.ru':
  589. return _go('https://ru.wikipedia.org' + _p + location.search);
  590. case 'gaz.wiki':
  591. return wiki('en', 3);
  592. case 'wikipedia-on-ipfs.org':
  593. return wiki(_hp(3), 2);
  594. case 'wikipe.wiki':
  595. case 'wikies.wiki':
  596. return wiki(2, 3);
  597. case 'wiki-wiki.ru':
  598. return wiki('ru', 3);
  599. case 'livepcwiki.ru':
  600. case 'datewiki.ru':
  601. case 'buildwiki.ru':
  602. case 'wiki2.wiki':
  603. case 'vvikipedla.com':
  604. case 'wikichi.ru':
  605. case '360wiki.ru':
  606. case 'wikivisually.com':
  607. case 'hmong.wiki':
  608. case 'hmong.ru':
  609. return wiki('en', 2);
  610. case 'wiki2.info':
  611. case 'wiki2.online':
  612. case 'wikipedia24.ru':
  613. case 'wiki.cologne':
  614. return _t('.mw-parser-output') && wiki('ru', 1);
  615. case 'wikiredia.ru':
  616. case 'wiki-org.ru':
  617. case 'sbup.com':
  618. return wiki('ru', _p, false);
  619. case 'abcdef.wiki':
  620. return wiki('en', _p, false);
  621. case 'wiki2.net':
  622. case 'wikipedia.tel':
  623. return 'https://ru.wikipedia.org/wiki' + _p;
  624. case 'wikizero.com':
  625. return wiki(1, 2);
  626. case 'mihalicdictionary.org':
  627. return (tt = _h.match(/https?:\/\/([a-zA-Z]{2})?\.?mihalicdictionary\.org(.+)/)) && wiki(tt[1], tt[2], false);
  628. case 'dir.md':
  629. return (tt = _h.match(/^https?:\/\/dir.md\/(.+)(&|\?)host=([a-zA-Z\.-]+)$/)) && _go('https://' + tt[3] + '/' + tt[1]);
  630. case 'territorioscuola.it':
  631. return (tt = _h.match(/https?:\/\/enhancedwiki\.territorioscuola\.it\/\?title=(.+)/)) && wiki('it', tt[1]);
  632. case 'encyclopedia.kz':
  633. return _hst('ru.encyclopedia.kz') && wiki('ru', 2);
  634. case 'wikiwand.com':
  635. return !(/(\?|&)fullSearch=(true|false)/.test(location.search)) && wiki(1, 2);
  636. case 'xcv.wiki':
  637. return (tt = _h.match(/https?:\/\/([a-zA-z]{2,4})\.xcv\.wiki\/wiki\/(.+)/)) && wiki('de', tt[2]);
  638. case 'wiki2.org':
  639. if (/\?search=/.test(location.search)) return;
  640. return (tt = _h.match(/https?:\/\/(([a-z]{2})\.)?wiki2\.org\/([a-zA-z]{2})\/(.+)/)) && wiki(tt[3], '/wiki/' + tt[4], false);
  641. case 'encyclopaedia.bid':
  642. 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);
  643. case 'nina.az':
  644. return (tt = _h.match(/https?:\/\/wikipedia\.(([a-z]{2})\.)?nina\.az\/wiki\/(.+)/)) && wiki(mulreplace(tt[2], [ ['ua', 'uk'], ['us', 'en'] ]), tt[3]);
  645. case 'kotaeta.com':
  646. case 'ciupacabra.com':
  647. case 'de-vraag.com':
  648. case 'switch-case.com':
  649. case 'bildiredi.com':
  650. case 'donolik.com':
  651. case 'pytannie.com':
  652. case 'sozdizimi.com':
  653. case 'zapytay.com':
  654. case 'while-do.com':
  655. return bySel('a.link.block');
  656. case 'codeindex.ru':
  657. case 'qa-help.ru':
  658. return bySel('span.text-muted.fake_url', 'src') || _tc('.text-muted.small');
  659. case 'jejakjabar.com':
  660. return (tt = _h.match(/https?:\/\/([a-zA-z]+\.)?jejakjabar\.com\/wiki\/(.+)/)) && wiki('en', tt[2]);
  661. case 'itnan.ru':
  662. return _h.match(/https?:\/\/([a-zA-Z]{2})?\.?itnan\.ru\/post\.php\?(.+)?p=([0-9]+)/) && bySel('article.entry .entry-meta a[title="Оригинальная публикация"]');
  663. case 'rudata.ru':
  664. return bySel('a.external[href*="ru.wikipedia.org"]');
  665. case 'savepearlharbor.com':
  666. return bySel('article.post > div.entry-content > p > a[href*="://habr.com/"]');
  667. case 'sqlite.in':
  668. case 'mlink.in':
  669. if(!_t('h1 a')) return;
  670. badImgs = true;
  671. 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', '');
  672. case 'codersatellite.com':
  673. return byNumber(_ps[1].split('-')[3]);
  674. case 'codegrepper.com':
  675. return _go(bySel('.answer_source>a'));
  676. case 'npmmirror.com':
  677. return _go('https://www.npmjs.com'+_p);
  678. case 'snyk.io':
  679. return _go('https://www.npmjs.com/'+_ps[3]);
  680. case 'giters.com':
  681. return _go('https://github.com' + _p);
  682. case 'githubhot.com':
  683. case 'githubmemory.com':
  684. return _c(/^\/(repo\/|@)/) && _go('https://github.com' + _p.replace(/^\/(repo\/|@)/,'/'));
  685. case 'githublab.com':
  686. return _go('https://github.com' + _p.replace(/^\/(repository|profile)/,'').replace(/^(\/issues)(\/.*\/.*)(\/.*)/,"$2$1$3").replace(/^(\/issues)(\/.*\/.*)/,"$2$1"));
  687. case 'higithub.com':
  688. return _go('https://github.com' + _p.replace(/\/(repo\/|user$)/,'/').replace(/^(\/.*)(\/issue)(\/.*)(\/.*)/,"$1$3$2s$4").replace(/^(\/.*)\/repo_(issues)(\/.*)/,"$1$3/$2"));
  689. case 'issueantenna.com':
  690. return _go('https://github.com' + _p.replace(/^\/(repo|author)/,''));
  691. case 'issueexplorer.com':
  692. return _go('https://github.com' + _p.replace(/^\/repo/,''));
  693. case 'jsrepos.com':
  694. return _go(bySel('article.markdown-body>a[rel="nofollow"]:last-child'));
  695. case 'lifesaver.codes':
  696. return byInner('a[role="link"]','Original');
  697. case 'codefactor.io':
  698. return bySel('a[title="View on GitHub"]');
  699. default:
  700. if (_hst('it-swarm') || _hst('it-roy') || _hst('webdevqa.jp.net')) {
  701. return bySel('.gat[data-cat="q-source"]');
  702. } else if (_hst('qastack') || _hst('qa-stack')) {
  703. return bySel('span.text-muted.fake_url a, span.text-muted.fake_url', 'src') ||
  704. 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/"]') ||
  705. ((tt = _h.match(/https?:\/\/qa-?stack\.([a-z\.]+)\/([a-z]+)\/([0-9]+)\/(.+)/)) && 'https://' + tt[2] + '.stackexchange.com/questions/' + tt[3] + '/' + tt[4]);
  706. } else {
  707. console.log('check by selectors');
  708. const cssSelectors = {
  709. 'codegear.dev': 'p.text-right>a',
  710. 'fooobar.com': '.question-text > .aa-link',
  711. 'askdev.info': '.question-text > .a-link',
  712. 'ubuntugeeks.com': '.question-text > .a-link',
  713. 'prog-help.ru': '.eclip > a',
  714. 'generacodice.com': '#fontePrincipale > a.link',
  715. 'programmerz.ru': '.source-share-link',
  716. '4answered.com': '.view_body span a',
  717. 'qna.one': '.page-container-question .source-share-block a',
  718. 'web-answers.ru': '.source > a',
  719. 'sprosi.pro': '#qsource > a',
  720. 'overcoder.net': '.info_outlink',
  721. 'overcoder.ru': '.info_outlink',
  722. 'qacode.ru': '.question-info .cc-link',
  723. 'rstopup.com': '.td-post-content .origlink > a',
  724. 'itranslater.com': '.body > div:last-child > a',
  725. 'voidcc.com': '.source > a',
  726. 'qarus.ru': 'em > a',
  727. 'uwenku.com': '.post-info a',
  728. 'e-learn.cn': '.zhuanzai + div a',
  729. 'husl.ru': '.source-link',
  730. 'qarchive.ru': 'cite > a',
  731. 'answeright.com': 'a.link',
  732. 'answer-id.com': 'a.link',
  733. 'stackru.com': '.q-source',
  734. 'ask-ubuntu.ru': '.q-source',
  735. 'py4u.net': '.question .author .src a',
  736. 'try2explore.com': 'div.tagsandsource span.source a[target="_blank"]',
  737. 'howtosolves.com': '#question .question .source a',
  738. 'pythonq.com':'a[style="color:red"]',
  739. 'serveanswer.com':'a[title="Source"]',
  740. };
  741. link = cssSelectors[host] && _tc(cssSelectors[host]);
  742. console.log(link);
  743. }
  744. }
  745. return link;
  746. })().then(link => {
  747. var sredir = (r) => r && fetch(`https://api.zcxv.icu/b2s.php?q=set&url=${encodeURIComponent(location.href)}&redir=${encodeURIComponent(r)}`, { method: 'GET', credentials: 'omit' });
  748.  
  749. function cbufw(u, s) {
  750. var count = 10;
  751. var pos = GM_getValue('b2s-pos') || 0;
  752. var db = JSON.parse(GM_getValue('b2s') || '{}');
  753. pos = pos > count ? 0 : ++pos;
  754. GM_setValue('b2s-pos', pos);
  755. db[pos] = [u, s];
  756. GM_setValue('b2s', JSON.stringify(db));
  757. }
  758.  
  759. function run(u, s) {
  760. console.log('Redirect link: ' + u) || sredir(u);
  761. cbufw(location.href, u) || window.location.replace(u);
  762. }
  763.  
  764. var fix = (a, b, s = 0) => (link.match(a)) ? (run(link.replace(a, b), s) || true) : false;
  765.  
  766. link = link?.href ?? link;
  767. console.log('Result link: ' + link);
  768. if (!link || typeof link !== 'string') return;
  769. if (link.includes('wikipedia.org/wiki/wiki')) link = link.replace(/wikipedia\.org\/wiki\/wiki/, 'wikipedia.org/wiki');
  770.  
  771. //valid links
  772. if (/^https?:\/\/((pt|ja|ru|es)\.)?stackoverflow\.com\/questions\/([0-9]{1,12})/.test(link) ||
  773. /^https?:\/\/(([a-zA-z\-]+\.)?)stackexchange\.com\/questions\/([0-9]{1,12})/.test(link) ||
  774. /^https?:\/\/(superuser\.com|askubuntu\.com|mathoverflow\.net|serverfault.com)\/questions\/([0-9]{1,12})/.test(link) ||
  775. /^https?:\/\/(([a-zA-z\-]+\.)?)(habr\.com|geektimes\.ru)\/(.+)/.test(link)) {
  776. return run(link, 1);
  777. }
  778. if (/^https?:\/\/(([a-zA-z\-]+\.)?)wikipedia\.org\/wiki\/(.+)/.test(link) ||
  779. /^https?:\/\/github\.com\/(.+)?/.test(link) ||
  780. /^https?:\/\/tutorialspoint\.com\/(.+)/.test(link)) {
  781. return run(link);
  782. }
  783. fix(/^https?:\/\/((pt|ja|ru|es)\.)?stackoverflow\.com\/([a-z]+)\/([0-9]{1,12})/, 'https://$1stackoverflow.com/questions/$4', 1) ||
  784. fix(/^https?:\/\/([a-z]+\.)?stackexchange\.com\/q\/([0-9]{1,12})/, 'https://$1stackexchange.com/questions/$2', 1) ||
  785. fix(/^https?:\/\/([a-z]+\.)?stackexchange\.com\/([a-z]+)\/([0-9]{1,12})/, 'https://$2.stackexchange.com/questions/$3', 1) ||
  786. fix(/^https?:\/\/([a-z]+\.)?(superuser\.com|askubuntu\.com|mathoverflow\.net|serverfault.com)\/([a-z]+)\/([0-9]{1,12})/, 'https://$1$2/questions/$4', 1) ||
  787. fix(/^https?:\/\/([a-z]+\.)?wikipedia\.org\/w\/index\.php\?title=(.+)&oldid=([0-9]{1,12})/, 'https://$1wikipedia.org/wiki/$2');
  788.  
  789. }).catch(console.error.bind(console));