wiki-reference

Позволяет генерировать ссылки в формате {{статья}} и {{книга}} для ру-вики

  1. // ==UserScript==
  2. // @name wiki-reference
  3. // @namespace torvin
  4. // @include https://www.ncbi.nlm.nih.gov/pubmed/*
  5. // @include http://www.ncbi.nlm.nih.gov/pubmed/*
  6. // @include http://adsabs.harvard.edu/abs/*
  7. // @include http://adsabs.harvard.edu/doi/*
  8. // @include http://ufn.ru/ru/articles/*
  9. // @include http://books.google.*/books*
  10. // @include https://books.google.*/books*
  11. // @include http://www.sciencedirect.com/science/article/*
  12. // @include http://gen.lib.rus.ec/scimag/*
  13. // @include http://onlinelibrary.wiley.com/doi/*
  14. // @include http://www.jstor.org/stable/*
  15. // @include http://www.jstor.org/discover/*
  16. // @include http://projecteuclid.org/euclid.*
  17. // @include https://projecteuclid.org/euclid.*
  18. // @include http://link.springer.com/*
  19. // @include http://www.mathnet.ru/php/archive.phtml*wshow=paper*
  20. // @include http://elibrary.ru/item.asp*
  21. // @require https://code.jquery.com/jquery-2.1.1.min.js
  22. // @version 1.8.8
  23. // @description Позволяет генерировать ссылки в формате {{статья}} и {{книга}} для ру-вики
  24. // @grant GM_xmlhttpRequest
  25. // @grant GM_setClipboard
  26. // ==/UserScript==
  27.  
  28. const templates = {
  29. article: 'статья',
  30. book: 'книга',
  31. };
  32.  
  33. const templateAdditionalArgs = {
  34. 'статья': [ 'ref', 'archiveurl', 'archivedate' ],
  35. 'книга': [ 'ref' ],
  36. };
  37.  
  38. const refTemplateParamOrder = [
  39. 'автор', 'часть', 'ссылка часть', 'заглавие', 'ссылка', 'ответственный',
  40. 'издание', 'издательство', 'год', 'серия', 'том', 'номер', 'страницы', 'страниц',
  41. 'язык', 'doi', 'bibcode', 'arxiv', 'isbn', 'archiveurl', 'archivedate', 'ref'
  42. ];
  43.  
  44. var Template = function(name) {
  45. var attrs = [];
  46.  
  47. this.add = function(name, value) {
  48. attrs.push({
  49. name: name,
  50. value: value
  51. });
  52. return this;
  53. };
  54.  
  55. this.get = function(name) {
  56. return attrs.filter(a => a.name === name).map(a => a.value)[0];
  57. }
  58.  
  59. this.getParamNames = function() {
  60. return attrs.map(a => a.name);
  61. }
  62.  
  63. this.getName = function() {
  64. return name;
  65. }
  66.  
  67. var getAttr = function(x) {
  68. return "|" + x.name + (x.value === undefined ? "" : " = " + x.value);
  69. };
  70.  
  71. this.toWiki = function() {
  72. if (attrs.length == 1)
  73. return "{{" + name + getAttr(attrs[0]) + "}}";
  74. else
  75. return "{{" + name + "\n" + attrs.map(a => " " + getAttr(a)).join("\n") + "\n}}";
  76. };
  77. };
  78.  
  79. var getText = function(node) {
  80. return node instanceof Element ? node.textContent : node;
  81. };
  82.  
  83. var clone = function(obj) {
  84. var target = {};
  85. for (var i in obj) {
  86. var value = obj[i];
  87. if (value instanceof Function)
  88. ;
  89. else if (typeof value == 'string')
  90. ;
  91. else
  92. value = clone(value);
  93. target[i] = value;
  94. }
  95. return target;
  96. }
  97.  
  98. var getWpFromXml = function(name, rules, xml) {
  99. var article = new Template(name);
  100.  
  101. for(var name in rules) {
  102. var rule = rules[name];
  103. article.add(name, rule.const ||
  104. Array.slice(xml.querySelectorAll(rule.selector)).map(function(node) {
  105. if (rule.map)
  106. node = rule.map(node);
  107. return Array.isArray(node) ? node.map(getText) : getText(node);
  108. }).map(function(item) {
  109. return rule.mapText ? rule.mapText(item) : item;
  110. }).join(rule.separator || ', ')
  111. );
  112. }
  113.  
  114. return getRef(article);
  115. };
  116.  
  117. var Parser = function(sourceText, index, tokens) {
  118. var _match;
  119. var _isEof;
  120.  
  121. //var _tokenRegex = /(\s*)(\{|\}|,|=|\\\W|\\[\w]+|[^\{\},\\=\s])/g;
  122. var _tokenRegex = new RegExp("(\\s*)(" + tokens.map(function(x) { return x.source || x }).join('|') + ")", 'g');
  123. _tokenRegex.lastIndex = index;
  124.  
  125. var getToken = function() {
  126. if (_isEof)
  127. throw new Error("EOF");
  128.  
  129. var index = _tokenRegex.lastIndex;
  130.  
  131. var res = _tokenRegex.exec(sourceText);
  132. if (!res) {
  133. _isEof = true;
  134. return null;
  135. }
  136. _match = {
  137. match: res[0],
  138. token: res[2],
  139. space: res[1].replace(/\s+/g, ' '),
  140. index: index,
  141. }
  142. }
  143.  
  144. this.matchAny = function() {
  145. var res = _match;
  146. getToken();
  147. return res;
  148. }
  149.  
  150. this.match = function(str) {
  151. if (_match.token !== str)
  152. throw new Error("Parser error at pos " + _tokenRegex.lastIndex + ". Expected '" + str + "', found '" + _match.token + "'.");
  153. return this.matchAny();
  154. }
  155.  
  156. this.matchIgnoreCase = function(str) {
  157. if (_match.token.toUpperCase() !== str.toUpperCase())
  158. throw new Error("Parser error at pos " + _tokenRegex.lastIndex + ". Expected '" + str + "', found '" + _match.token + "'.");
  159. return this.matchAny();
  160. }
  161.  
  162. this.matchAnyIgnoreCase = function(strs) {
  163. if (strs.every(function(str) { return _match.token.toUpperCase() !== str.toUpperCase(); }))
  164. throw new Error("Parser error at pos " + _tokenRegex.lastIndex + ". Expected any of: '" + strs.join("', '") + "', found '" + _match.token + "'.");
  165. return this.matchAny();
  166. }
  167.  
  168. this.matchNot = function(strs) {
  169. if (strs.indexOf(_match.token) != -1)
  170. throw new Error("Parser error at pos " + _tokenRegex.lastIndex + ". Unexpected '" + _match.token + "'.");
  171. return this.matchAny();
  172. }
  173.  
  174. this.index = function() {
  175. return _match.index;
  176. }
  177.  
  178. this.isEof = () => _isEof;
  179.  
  180. Object.defineProperty(this, "token", { get: function() { return _match.token; }});
  181.  
  182. getToken();
  183. }
  184.  
  185. var Bibtex = function(sourceText, index) {
  186. var _plainText = /[^\{\},\\=\s"\$]+/;
  187. const _tokens = [
  188. /\{/,
  189. /\}/,
  190. /"/,
  191. /,/,
  192. /=/,
  193. /@/,
  194. /\$/,
  195. /\\\W/,
  196. /\\[\w]+/,
  197. _plainText,
  198. ];
  199.  
  200. var _parser = new Parser(sourceText, index || 0, _tokens);
  201.  
  202. var entry = function() {
  203. _parser.match("{");
  204. var id = fieldValue();
  205. _parser.match(",");
  206. var f = fields();
  207. _parser.match("}");
  208.  
  209. f.bibcode = id;
  210. return f;
  211. }
  212.  
  213. var comment = function() {
  214. _parser.match("{");
  215. var text = fieldValue();
  216. _parser.match("}");
  217. return { comment: text };
  218. }
  219.  
  220. var type = function(entryTypes) {
  221. _parser.match('@');
  222. var token = _parser.token;
  223. if (entryTypes.length)
  224. _parser.matchAnyIgnoreCase(entryTypes);
  225. else
  226. _parser.matchAny();
  227. return token;
  228. }
  229.  
  230. var fields = function() {
  231. var res = {};
  232. for(;;) {
  233. var f = field();
  234. res[f.name] = f.value;
  235. if (_parser.token !== ",") break;
  236. _parser.match(",");
  237. if (_parser.token === "}") break;
  238. }
  239. return res;
  240. }
  241.  
  242. var quoted = function() {
  243. return _parser.match("{").space + quotedValue() + _parser.match("}").space;
  244. }
  245.  
  246. var diacritics = {
  247. '`': '\u0300',
  248. '\'': '\u0301',
  249. '^': '\u0302',
  250. '~': '\u0303',
  251. '=': '\u0304',
  252. 'u': '\u0306',
  253. '.': '\u0307',
  254. '"': '\u0308',
  255. 'r': '\u030a',
  256. 'H': '\u030b',
  257. 'v': '\u030c',
  258. 'c': '\u0327',
  259. 'k': '\u0328',
  260. 'd': '\u0323',
  261. 'b': '\u0331',
  262. 't': '\u0361',
  263. };
  264.  
  265. var ligatures = {
  266. 'L': '\u0141',
  267. 'l': '\u0142',
  268. 'AA': '\u00c5',
  269. 'aa': '\u00e5',
  270. 'AE': '\u00c6',
  271. 'ae': '\u00e6',
  272. 'O': '\u00d8',
  273. 'o': '\u00f8',
  274. 'OE': '\u0152',
  275. 'oe': '\u0153',
  276. 'i': '\u0131',
  277. 'j': '\u0237',
  278. 'ss': '\u00df',
  279. };
  280.  
  281. var entity = function() {
  282. if (_parser.token[0] != '\\')
  283. throw new Error("Expected entity, found " + _parser.token);
  284.  
  285. var cmd = _parser.matchAny().token.substr(1);
  286.  
  287. var value = ligatures[cmd];
  288. if (value)
  289. return value;
  290.  
  291. value = diacritics[cmd];
  292. if (value)
  293. return fieldValue() + value;
  294.  
  295. return cmd;
  296. }
  297.  
  298. var quotedValue = function() {
  299. var res = "";
  300. for(;;) {
  301. if (_parser.token === "{")
  302. res += quoted();
  303. else if (_parser.token === "}")
  304. break;
  305. else if (_parser.token[0] === '\\')
  306. res += entity();
  307. else
  308. res += plainText();
  309. }
  310. return res;
  311. }
  312.  
  313. var plainText = function() {
  314. return _parser.matchAny().match.replace(/---?/g, '—').replace(/~/g, ' ');
  315. }
  316.  
  317. var fieldValue = function() {
  318. var res = "";
  319. for(;;) {
  320. if (_parser.token == "{")
  321. res += quoted();
  322. else if (_parser.token == '"')
  323. res += doubleQuoted();
  324. else if (_parser.token[0] === '\\')
  325. res += entity();
  326. else if (_parser.token === '$')
  327. res += math();
  328. else if (_plainText.test(_parser.token))
  329. res += plainText();
  330. else
  331. break;
  332. }
  333. return res.trim();
  334. }
  335.  
  336. var amsFieldValue = function() {
  337. var res = "";
  338. for(;;) {
  339. if (_parser.isEof())
  340. break;
  341. else if (_parser.token == "{")
  342. res += quoted();
  343. else if (_parser.token == '"')
  344. res += doubleQuoted();
  345. else if (_parser.token[0] === '\\')
  346. res += entity();
  347. else if (_parser.token === '$')
  348. res += math();
  349. else
  350. res += plainText();
  351. }
  352. return res.trim();
  353. }
  354.  
  355. var math = function() {
  356. var before = _parser.match('$').space;
  357. var res = '';
  358. for(;;) {
  359. if (_parser.token === '$')
  360. break;
  361. else
  362. res += _parser.matchAny().match;
  363. }
  364. return before + '<math>' + res + '</math>' + _parser.match('$').space;
  365. }
  366.  
  367. var doubleQuoted = function() {
  368. return _parser.match('"').space + doubleQuotedValue() + _parser.match('"').space;
  369. }
  370.  
  371. var doubleQuotedValue = function() {
  372. var res = "";
  373. for(;;) {
  374. if (_parser.isEof())
  375. throw new Error("Unexpected EOF.");
  376. else if (_parser.token == "{")
  377. res += quoted();
  378. else if (_parser.token == '"')
  379. break;
  380. else if (_parser.token[0] === '\\')
  381. res += entity();
  382. else if (_parser.token === '$')
  383. res += math();
  384. else
  385. res += plainText();
  386. }
  387. return res;
  388. }
  389.  
  390. var field = function() {
  391. var name = fieldValue();
  392. _parser.match("=");
  393. var value = fieldValue();
  394.  
  395. return {
  396. name: name,
  397. value: value
  398. }
  399. }
  400.  
  401. this.index = function() {
  402. return _parser.index();
  403. }
  404.  
  405. this.parse = function(entryTypes) {
  406. if (!entryTypes)
  407. entryTypes = []
  408. else if (typeof entryTypes == 'string' || entryTypes instanceof String)
  409. entryTypes = [ entryTypes ];
  410. var realType = type(entryTypes);
  411.  
  412. if (realType.toLowerCase() == 'comment')
  413. var result = comment();
  414. else
  415. var result = entry();
  416.  
  417. result.type = realType;
  418. return result;
  419. }
  420.  
  421. this.parseAmsFieldValue = function() {
  422. return amsFieldValue();
  423. }
  424. }
  425.  
  426. var bibtexBase = {
  427. 'автор': {
  428. selector: 'author',
  429. map: (text) => authorsToWiki(getAuthors(text)),
  430. },
  431. 'заглавие': {
  432. selector: 'title',
  433. map: function(text) {
  434. return text.replace(/^"|"$/g, '')
  435. }
  436. },
  437. 'издание': {
  438. selector: 'journal',
  439. map: function(text) {
  440. return {
  441. aj: 'Astronomical Journal',
  442. actaa: 'Acta Astronomica',
  443. araa: 'Annual Review of Astron and Astrophys',
  444. apj: 'Astrophysical Journal',
  445. apjl: 'Astrophysical Journal, Letters',
  446. apjs: 'Astrophysical Journal, Supplement',
  447. ao: 'Applied Optics',
  448. apss: 'Astrophysics and Space Science',
  449. aap: 'Astronomy and Astrophysics',
  450. aapr: 'Astronomy and Astrophysics Reviews',
  451. aaps: 'Astronomy and Astrophysics, Supplement',
  452. azh: 'Astronomicheskii Zhurnal',
  453. baas: 'Bulletin of the AAS',
  454. caa: 'Chinese Astronomy and Astrophysics',
  455. cjaa: 'Chinese Journal of Astronomy and Astrophysics',
  456. icarus: 'Icarus',
  457. jcap: 'Journal of Cosmology and Astroparticle Physics',
  458. jrasc: 'Journal of the RAS of Canada',
  459. memras: 'Memoirs of the RAS',
  460. mnras: 'Monthly Notices of the RAS',
  461. na: 'New Astronomy',
  462. nar: 'New Astronomy Review',
  463. pra: 'Physical Review A: General Physics',
  464. prb: 'Physical Review B: Solid State',
  465. prc: 'Physical Review C',
  466. prd: 'Physical Review D',
  467. pre: 'Physical Review E',
  468. prl: 'Physical Review Letters',
  469. pasa: 'Publications of the Astron. Soc. of Australia',
  470. pasp: 'Publications of the ASP',
  471. pasj: 'Publications of the ASJ',
  472. rmxaa: 'Revista Mexicana de Astronomia y Astrofisica',
  473. qjras: 'Quarterly Journal of the RAS',
  474. skytel: 'Sky and Telescope',
  475. solphys: 'Solar Physics',
  476. sovast: 'Soviet Astronomy',
  477. ssr: 'Space Science Reviews',
  478. zap: 'Zeitschrift fuer Astrophysik',
  479. nat: 'Nature',
  480. iaucirc: 'IAU Cirulars',
  481. aplett: 'Astrophysics Letters',
  482. apspr: 'Astrophysics Space Physics Research',
  483. bain: 'Bulletin Astronomical Institute of the Netherlands',
  484. fcp: 'Fundamental Cosmic Physics',
  485. gca: 'Geochimica Cosmochimica Acta',
  486. grl: 'Geophysics Research Letters',
  487. jcp: 'Journal of Chemical Physics',
  488. jgr: 'Journal of Geophysics Research',
  489. jqsrt: 'Journal of Quantitiative Spectroscopy and Radiative Transfer',
  490. memsai: 'Mem. Societa Astronomica Italiana',
  491. nphysa: 'Nuclear Physics A',
  492. physrep: 'Physics Reports',
  493. physscr: 'Physica Scripta',
  494. planss: 'Planetary Space Science',
  495. procspie: 'Proceedings of the SPIE',
  496. }[text] || text;
  497. },
  498. },
  499. 'год': {
  500. selector: 'year',
  501. },
  502. 'номер': {
  503. selector: 'number',
  504. },
  505. 'том': {
  506. selector: 'volume',
  507. },
  508. 'страницы': {
  509. selector: 'pages',
  510. },
  511. 'издательство': {
  512. selector: 'publisher',
  513. },
  514. 'issn': {
  515. selector: 'issn',
  516. },
  517. 'doi': {
  518. selector: 'doi',
  519. },
  520. 'arxiv': {
  521. selector: 'eprint',
  522. map: function(text) {
  523. const prefix = 'arXiv:';
  524. if (text.indexOf(prefix) == 0)
  525. text = text.substr(prefix.length);
  526. return text;
  527. }
  528. },
  529. 'ссылка': { const: "" },
  530. 'язык': { const: "en" },
  531. };
  532.  
  533. var getAuthors = function(text) {
  534. return text.split(' and ').map(function(name) {
  535. return name.replace(',', '');
  536. });
  537. };
  538.  
  539. var authorsToWiki = function(authors) {
  540. return authors.map(function(name) {
  541. return new Template('nobr').add(name).toWiki()
  542. }).join(', ');
  543. };
  544.  
  545. var switchAuthorNameParts = function(name) {
  546. var match = /^(.+) (\S+)$/.exec(name);
  547. if (!match) return name;
  548. return match[2] + ' ' + match[1];
  549. }
  550.  
  551. var getWpFromObj = function(name, rules, obj) {
  552. var article = new Template(name);
  553.  
  554. for(var name in rules) {
  555. var rule = rules[name];
  556.  
  557. var value;
  558. if (rule.const !== undefined)
  559. value = rule.const
  560. else {
  561. if (typeof rule.selector === "function")
  562. value = rule.selector(obj);
  563. else
  564. value = obj[rule.selector];
  565. if (!value)continue;
  566. }
  567.  
  568. if (rule.map)
  569. value = rule.map(value, obj);
  570.  
  571. article.add(name, value);
  572. }
  573.  
  574. return getRef(article);
  575. }
  576.  
  577. var getRef = function(template) {
  578. // adding additional args
  579. for(var a of templateAdditionalArgs[template.getName()] || []) {
  580. if (template.get(a) === undefined)
  581. template.add(a, '');
  582. }
  583.  
  584. // ordering
  585. var orderedTemplate = new Template(template.getName());
  586. var names = template.getParamNames();
  587. var getOrder = x => {
  588. var i = refTemplateParamOrder.indexOf(x.toLowerCase());
  589. return i === -1 ? 99999 : i;
  590. }
  591. names.sort((x, y) => getOrder(x) - getOrder(y));
  592.  
  593. for(var name of names)
  594. orderedTemplate.add(name, template.get(name));
  595.  
  596. return orderedTemplate.toWiki();
  597. }
  598.  
  599. var testUrl = function(regex) {
  600. return regex.exec(window.location.href)
  601. }
  602.  
  603. var createWpButton = function(getResult, tag, param) {
  604. tag = tag || 'a';
  605. return $('<' + tag + '>')
  606. .attr('href', '#')
  607. .text('WP')
  608. .click(function(e) {
  609. e.preventDefault();
  610.  
  611. var promise = $.Deferred();
  612.  
  613. promise.done(function(result) {
  614. showResult(result);
  615. }).fail(function(result) {
  616. alert(result);
  617. });
  618.  
  619. getResult({
  620. resolve: function(result) {
  621. promise.resolve(result)
  622. },
  623. reject: function(result) {
  624. promise.reject(result)
  625. },
  626. }, param);
  627. })
  628. .get(0);
  629. }
  630.  
  631. var showResult = function(text) {
  632. var button;
  633.  
  634. var dialog = $('<div>').css({
  635. 'position': 'fixed',
  636. 'top': 0,
  637. 'left': 0,
  638. 'width': '100%',
  639. 'height': '100%',
  640. 'z-index': 9999999,
  641. }).appendTo(document.body).append(
  642. // dimmer
  643. $('<div>').css({
  644. 'width': '100%',
  645. 'height': '100%',
  646. 'background-color': 'black',
  647. 'opacity': 0.6,
  648. })
  649. ).append(
  650. // dialog container
  651. $('<div>').css({
  652. 'display': 'table',
  653. 'position': 'absolute',
  654. 'top': 0,
  655. 'left': 0,
  656. 'width': '100%',
  657. 'height': '100%',
  658. 'font': '12px sans-serif',
  659. }).append(
  660. $('<div>').css({
  661. 'text-align': 'center',
  662. 'display': 'table-cell',
  663. 'vertical-align': 'middle',
  664. }).append(
  665. // dialog
  666. $('<div>').css({
  667. 'padding': 10,
  668. 'background-color': 'white',
  669. 'display': 'inline-block',
  670. 'max-width': '80%',
  671. }).append(
  672. // text
  673. $('<div>').css({
  674. 'padding': 5,
  675. 'white-space': 'pre-wrap',
  676. 'text-align': 'left',
  677. }).text(text)
  678. ).append(
  679. // buttons
  680. $('<div>').css({
  681. 'text-align': 'right',
  682. 'margin-top': 10,
  683. }).append(
  684. button = $('<button>').text('Copy & close').click(function() {
  685. GM_setClipboard(text);
  686. dialog.remove();
  687. }).focus()
  688. ).append(
  689. $('<button>').text('Cancel').click(function() {
  690. dialog.remove();
  691. })
  692. )
  693. )
  694. )
  695. )
  696. );
  697.  
  698. button.focus();
  699. };
  700.  
  701. var getFormData = function(data) {
  702. if (data && typeof(data) !== 'string') {
  703. var params = [];
  704. for(var k in data) {
  705. params.push(encodeURIComponent(k) + '=' + encodeURIComponent(data[k]))
  706. }
  707. data = params.join('&');
  708. }
  709. return data;
  710. }
  711.  
  712. var ajaxGet = function(url, process) {
  713. var data;
  714. if (!(typeof url === 'string' || url instanceof String)) {
  715. var { url, data } = url;
  716. }
  717.  
  718. if (data)
  719. url = url + '?' + getFormData(data);
  720.  
  721. return ajax({
  722. method: 'GET',
  723. url: url,
  724. }, process);
  725. };
  726.  
  727. var ajaxPost = function({ url, data }, process) {
  728. return ajax({
  729. method: 'POST',
  730. url: url,
  731. data: getFormData(data),
  732. headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  733. }, process);
  734. };
  735.  
  736. var ajax = function(params, process) {
  737. var promise = $.Deferred();
  738.  
  739. params.onload = function(response) {
  740. try {
  741. promise.resolve(process(response.responseText));
  742. } catch(e) {
  743. promise.reject(e + '\n\n' + e.stack);
  744. }
  745. }
  746. setTimeout(function() {
  747. GM_xmlhttpRequest(params)
  748. }, 0);
  749.  
  750. return promise.promise();
  751. };
  752.  
  753. var getBibtex = function(params) {
  754. return function(text) {
  755. var bibtex = (params.parse || (t => new Bibtex(t).parse()))(text);
  756. var template = (params.getTemplate && params.getTemplate(bibtex)) || (params.templates || templates)[bibtex.type.toLowerCase()];
  757. if (!template) {
  758. throw new Error('unknown type: ' + bibtex.type);
  759. }
  760. return getWpFromObj(template, params.getRules(bibtex.type), bibtex);
  761. }
  762. };
  763.  
  764. var getXml = function(params) {
  765. return function(text) {
  766. var xml = params.parse(text);
  767. var template = params.getTemplate(xml);
  768. if (!template) {
  769. throw new Error('unknown type: ' + bibtex.type);
  770. }
  771. return getWpFromXml(template, params.getRules(xml), xml);
  772. }
  773. };
  774.  
  775. var proecssPage = function(page, res) {
  776. var onButton = function(promise, param) {
  777. try {
  778. $.when(page.act(res, param)).then(promise.resolve, promise.reject);
  779. } catch(e) {
  780. promise.reject(e + '\n\n' + e.stack);
  781. }
  782. };
  783.  
  784. page.attach(createWpButton.bind(null, onButton));
  785. };
  786.  
  787. (function(pages) {
  788. try {
  789. for(var page of pages) {
  790. var res = page.test();
  791. if (res) {
  792. proecssPage(page, res);
  793. break;
  794. }
  795. };
  796. } catch(e) {
  797. alert(e + '\n\n' + e.stack);
  798. }
  799. })([
  800. // pubmed
  801. {
  802. test: function() {
  803. return testUrl(/\/pubmed\/(\d+)\/?$/);
  804. },
  805. attach: function(createButton) {
  806. $('#messagearea').after(
  807. $('<div>').append(createButton())
  808. );
  809. },
  810. act: ([,id]) => ajaxGet({
  811. url: id,
  812. data: { dopt: 'Abstract', report: 'xml', format: 'text' },
  813. }, getXml({
  814. parse: function(text) {
  815. var html = new DOMParser().parseFromString(text, "text/html");
  816. return new DOMParser().parseFromString(html.body.textContent, "text/xml");
  817. },
  818. getTemplate: () => templates.article,
  819. getRules: () => ({
  820. 'автор': {
  821. selector: 'Article > AuthorList > Author',
  822. map: function(node) {
  823. var name = getText(node.querySelector('CollectiveName'));
  824. if (name) return name;
  825. name = [ node.querySelector('LastName'), node.querySelector('Initials') ].map(getText);
  826. return new Template('nobr').add(name[0] + ' ' + name[1].replace(/(.)/g, "$1. ").trim()).toWiki()
  827. },
  828. },
  829. 'заглавие': {
  830. selector: 'Article > ArticleTitle',
  831. mapText: function(text) { return text.replace(/^(.*?)\.?$/, "$1") },
  832. },
  833. 'издание': {
  834. selector: 'Article > Journal > Title',
  835. },
  836. 'год': {
  837. selector: 'Article > Journal > JournalIssue > PubDate',
  838. mapText: function(text) { return /^\s*(\d{4})/.exec(text)[1] },
  839. },
  840. 'номер': {
  841. selector: 'Article > Journal > JournalIssue > Issue',
  842. },
  843. 'том': {
  844. selector: 'Article > Journal > JournalIssue > Volume',
  845. },
  846. 'страницы': {
  847. selector: 'Article > Pagination > MedlinePgn',
  848. mapText: text => text.replace('-', '—'),
  849. },
  850. 'issn': {
  851. selector: 'Article > Journal > ISSN[IssnType=Electronic]',
  852. },
  853. 'doi': {
  854. selector: 'ArticleIdList > ArticleId[IdType=doi]',
  855. },
  856. 'pmid': {
  857. selector: 'ArticleIdList > ArticleId[IdType=pubmed]',
  858. },
  859. 'язык': { const: "en" },
  860. 'ссылка': { const: "" },
  861. }),
  862. })),
  863. },
  864. // adsabs
  865. {
  866. test: function() {
  867. return testUrl(/adsabs\.harvard\.edu\/(?:abs|doi)\/(.*)$/);
  868. },
  869. attach: function(createButton) {
  870. $('h3').eq(0)
  871. .append(document.createTextNode(' '))
  872. .append(createButton());
  873. },
  874. act: ([,id]) => ajaxGet({
  875. url: '/cgi-bin/nph-bib_query',
  876. data: { data_type: 'BIBTEX', bibcode: decodeURIComponent(id) },
  877. }, getBibtex({
  878. parse: function(text) {
  879. var index = text.search(/@\w+{/);
  880. if (index == -1)
  881. throw new Error('bibtex not found');
  882. return new Bibtex(text, index).parse([ 'article', 'book', 'inproceedings' ]);
  883. },
  884. getRules: function() {
  885. var bibtex = clone(bibtexBase);
  886. bibtex.bibcode = { selector: 'bibcode' };
  887. bibtex.издание = {
  888. selector: obj => obj['journal'] || obj['booktitle'],
  889. map: bibtex.издание.map,
  890. }
  891. return bibtex;
  892. },
  893. templates: Object.assign({ 'inproceedings': templates.article }, templates),
  894. })),
  895. },
  896. // ufn
  897. {
  898. test: function() {
  899. return testUrl(/\/ufn\.ru\/ru\/articles\/(\d+\/\d+\/\w+)\//);
  900. },
  901. attach: function(createButton) {
  902. $('#print > table tr > td').next().append(
  903. $('<td>').append(createButton())
  904. );
  905. },
  906. act: ([, id]) => ajaxGet('/ru/articles/' + id + '/citation/ru/bibtex.html', getBibtex({
  907. parse: function(text) {
  908. var html = new DOMParser().parseFromString(text, "text/html");
  909. var node = html.body.querySelector('.cit_code > pre');
  910. if (!node)
  911. throw new Error('bibtex not found');
  912. return new Bibtex(node.textContent).parse('article');
  913. },
  914. getRules: function() {
  915. var bibtex = clone(bibtexBase);
  916.  
  917. var nameRegex = /^(.+) (\S+)$/;
  918. bibtex.автор.map = function(text) {
  919. return authorsToWiki(getAuthors(text).map(switchAuthorNameParts));
  920. }
  921. delete bibtex.издательство;
  922. bibtex.страницы.map = t => t.replace('-', '—');
  923. bibtex.ссылка = { const: 'http://ufn.ru/ru/articles/' + id + '/' };
  924. bibtex.язык = { const: "ru" };
  925.  
  926. return bibtex;
  927. }
  928. })),
  929. },
  930. // google books
  931. {
  932. test: function() {
  933. return window.self == window.top && testUrl(/https?:\/\/books\.google\..+\/books.*[?&]id=([^&$]+)/);
  934. },
  935. attach: function(createButton) {
  936. var button = $(createButton()).addClass('gb-button').css('margin-left', '4px');
  937. $('.metadata_value > .gb-button:last-child, #gb-get-book-container > *:first-child')
  938. .parent()
  939. .append(button);
  940. },
  941. act: ([, id]) => ajaxGet({
  942. url: 'http://books.google.us/books/download/',
  943. data: { id: id, output: 'bibtex' },
  944. }, getBibtex({
  945. parse: t => new Bibtex(t).parse('book'),
  946. getRules: function() {
  947. var bibtex = clone(bibtexBase);
  948. bibtex.серия = { 'selector': 'series' };
  949. bibtex.страниц = { const: "" };
  950. bibtex.isbn = { 'selector': 'isbn' };
  951. bibtex.ссылка = { const: 'http://books.google.com/books?id=' + id };
  952. return bibtex;
  953. }
  954. })),
  955. },
  956. // sciencedirect
  957. {
  958. test: function() {
  959. return testUrl(/sciencedirect\.com\/science\/article\//);
  960. },
  961. attach: function(createButton) {
  962. $('#articleNav > ul').prepend($('<li />').append(
  963. $(createButton()).attr('type', 'button').val('WP').css({
  964. 'vertical-align': 'middle',
  965. 'line-height': '40px',
  966. })
  967. ));
  968. },
  969. act: function() {
  970. var params = {
  971. 'citation-type': 'BIBTEX'
  972. };
  973. $('form[name=exportCite] input[type=hidden]').each(function(i, hidden) {
  974. params[$(hidden).attr('name')] = $(hidden).val();
  975. });
  976. return ajaxPost({
  977. url: $('form[name=exportCite]').attr('action'),
  978. data: params,
  979. }, getBibtex({
  980. parse: t => new Bibtex(t).parse('article'),
  981. getRules: function() {
  982. var bibtex = clone(bibtexBase);
  983. bibtex.ссылка = { selector: 'url' };
  984. bibtex.doi.map = function(text) {
  985. var res = /http:\/\/dx\.doi\.org\/(.*)$/.exec(text) || [];
  986. return res[1] || text;
  987. }
  988. bibtex.страницы.map = function(text) {
  989. return text.replace(' - ', '—')
  990. }
  991. return bibtex;
  992. }
  993. }))
  994. },
  995. },
  996. // Library Genesis
  997. {
  998. test: function() {
  999. return testUrl(/gen\.lib\.rus\.ec\/scimag\//);
  1000. },
  1001. attach: function(createButton) {
  1002. $('a[href^="bibtex.php?"]').each((i, e) => {
  1003. var button = createButton('a', $(e).attr('href'));
  1004. button.style.fontWeight = 'bold';
  1005. $(e).after('<br />', button);
  1006. });
  1007. },
  1008. act: (_, url) => ajaxGet({
  1009. url: url,
  1010. }, getBibtex({
  1011. parse: text => {
  1012. var html = new DOMParser().parseFromString(text, "text/html");
  1013. return new Bibtex(html.querySelector('textarea#bibtext').value).parse('article');
  1014. },
  1015. getRules: function() {
  1016. var bibtex = clone(bibtexBase);
  1017. bibtex.номер = { selector: 'issue' };
  1018. bibtex.страницы = { selector: 'page' };
  1019. bibtex.язык = { const: '' };
  1020. return bibtex;
  1021. },
  1022. })),
  1023. },
  1024. // wiley
  1025. {
  1026. test: function() {
  1027. return testUrl(/onlinelibrary\.wiley\.com\/doi\//)
  1028. },
  1029. attach: function(createButton) {
  1030. $('#promosAndTools .titleTools').append($('<li>').append(createButton()));
  1031. },
  1032. act: function() {
  1033. var [, doi] = /^DOI:\s+(.+)$/.exec($('#doi').text().trim());
  1034. if (!doi) throw new Error('doi not found');
  1035.  
  1036. return ajaxPost({
  1037. url: '/documentcitationdownloadformsubmit',
  1038. data: { doi: doi, fileFormat: 'BIBTEX', hasAbstract: 'CITATION' },
  1039. }, getBibtex({
  1040. getRules: function() {
  1041. var bibtex = clone(bibtexBase);
  1042. bibtex.ссылка = { selector: 'url' };
  1043. return bibtex;
  1044. }
  1045. }))
  1046. },
  1047. },
  1048. // jstor
  1049. {
  1050. test: function() {
  1051. return testUrl(/www\.jstor\.org\/[^\/]+\/(.*?)($|\?)/)
  1052. },
  1053. attach: function(createButton) {
  1054. $('.action-buttons').append($('<li>').append($(createButton()).addClass('button button-jstor')));
  1055. },
  1056. act: ([, id]) => ajaxGet('/citation/text/' + id, getBibtex({
  1057. getRules: function() {
  1058. var bibtex = clone(bibtexBase);
  1059. bibtex.ссылка = { selector: 'URL' };
  1060. bibtex.номер.selector = function(obj) {
  1061. return obj['number'] || obj['jstor_issuetitle'];
  1062. }
  1063. bibtex.страницы.map = function(text) {
  1064. return text.replace(/^p?p\. /, "").replace('-', '—');
  1065. };
  1066. return bibtex;
  1067. },
  1068. })),
  1069. },
  1070. // projecteuclid
  1071. {
  1072. test: function() {
  1073. return testUrl(/projecteuclid.org\/(euclid\..*?\/\d+)/);
  1074. },
  1075. attach: function(createButton) {
  1076. $('#export-form').append($(createButton()).addClass('btn export-link-special'))
  1077. },
  1078. act: ([, id]) => ajaxPost({
  1079. url: '/export_citations',
  1080. data: { format: "bibtex", delivery: "browser", address: '', h: id },
  1081. }, getBibtex({
  1082. templates: {
  1083. 'article': templates.article,
  1084. 'inbook': templates.book,
  1085. },
  1086. getRules: function(type) {
  1087. var bibtex = clone(bibtexBase);
  1088. bibtex.издание = { selector: 'fjournal' };
  1089.  
  1090. if (type.toLowerCase() == 'inbook') {
  1091. bibtex.место = { selector: 'address' };
  1092. bibtex.часть = bibtex.заглавие;
  1093. bibtex.заглавие = { selector: 'booktitle' };
  1094. bibtex['ссылка часть'] = { selector: 'url' };
  1095. delete bibtex.ссылка;
  1096. } else {
  1097. bibtex.ссылка = { selector: 'url' };
  1098. }
  1099.  
  1100. return bibtex;
  1101. }
  1102. })),
  1103. },
  1104. // springer
  1105. {
  1106. test: () => testUrl(/\/link\.springer\.com\/([^#]+)/),
  1107. attach: function(createButton) {
  1108. if ($('#export-citation').length)
  1109. $('.other-actions > ul').append($('<li>').append(createButton()));
  1110. },
  1111. act: ([, id]) => ajaxGet('http://link.springer.com/export-citation/' + id + '.bib', getBibtex({
  1112. parse: text => new Bibtex(text.replace(/^@.*?{/, '$&x,')).parse(),
  1113. templates: {
  1114. 'article': templates.article,
  1115. 'incollection': templates.book,
  1116. },
  1117. getRules: function(type) {
  1118. var bibtex = clone(bibtexBase);
  1119. bibtex.страницы.map = t => t.replace('-', '—');
  1120. bibtex.ссылка = { const: 'http://link.springer.com/' + id };
  1121. if (type == 'incollection') {
  1122. bibtex.ответственный = {
  1123. selector: 'editor',
  1124. map: text => 'Ed. by ' + authorsToWiki(getAuthors(text)),
  1125. };
  1126. bibtex.серия = { 'selector': 'series' };
  1127. bibtex.часть = bibtex.заглавие;
  1128. bibtex.заглавие = { selector: 'booktitle' };
  1129. bibtex['ссылка часть'] = bibtex.ссылка;
  1130. bibtex.ссылка = { const: $('#about-link').attr('href') };
  1131. bibtex.isbn = { 'selector': 'isbn' };
  1132. } else if (type == 'article') {
  1133. delete bibtex.издательство;
  1134. }
  1135. return bibtex;
  1136. }
  1137. })),
  1138. },
  1139. // mathnet
  1140. {
  1141. test: () => testUrl(/\/www\.mathnet\.ru\//),
  1142. attach: function(createButton) {
  1143. $('#citPaperAMSBIBID').before(createButton());
  1144. },
  1145. act: () => getBibtex({
  1146. parse: t => {
  1147. var transl = false;
  1148. var res = {};
  1149. for(var line of t.split('\n')) {
  1150. if (!line) continue;
  1151. var [, key, value] = /^\\(\w+)\s*(.*)$/.exec(line);
  1152. if (key === 'transl' || key === 'rtransl') {
  1153. transl = true;
  1154. continue;
  1155. }
  1156. if (transl)
  1157. key = 'transl_' + key;
  1158. if (key == 'by')
  1159. value = value.replace(/([^\\]), /g, '$1 and ');
  1160. res[key] = new Bibtex(value).parseAmsFieldValue();
  1161. }
  1162. return res;
  1163. },
  1164. getTemplate: () => templates.article,
  1165. getRules: () => ({
  1166. 'автор': {
  1167. selector: 'by',
  1168. map: text => authorsToWiki(getAuthors(text).map(switchAuthorNameParts)),
  1169. },
  1170. 'заглавие': { selector: 'paper' },
  1171. 'издание': { selector: b => b.journalname || b.jour || b.serial },
  1172. 'год': { selector: 'yr' },
  1173. 'номер': { selector: 'issue' },
  1174. 'том': { selector: 'vol' },
  1175. 'страницы': { selector: 'pages' },
  1176. 'издательство': { selector: 'publ' },
  1177. 'ссылка': { selector: 'mathnet' },
  1178. 'язык': { const: 'ru' },
  1179. 'bibcode': {
  1180. selector: 'adsnasa',
  1181. map: url => /bib_query\?(.*)$/.exec(url)[1],
  1182. }
  1183. }),
  1184. })($('#citPaperAMSBIBID pre').text())
  1185. },
  1186. // elibrary
  1187. {
  1188. test: () => testUrl(/elibrary\.ru\/item.asp/),
  1189. attach: function(createButton) {
  1190. var space = $('.left-panel').eq(0).parents('tr').eq(0).next('tr');
  1191. $('<tr>').append(
  1192. $('<td>').append(
  1193. $('<div>').addClass('left-panel').append(
  1194. $(createButton()).css({
  1195. fontWeight: 'bold',
  1196. color: '#F26C4F',
  1197. })
  1198. )
  1199. )
  1200. ).insertAfter(space).after(space.clone());
  1201. },
  1202. act: () => {
  1203. var data = {};
  1204. const mapping = {
  1205. 'язык': {
  1206. value: v => ({
  1207. 'русский': 'ru',
  1208. 'английский': 'en',
  1209. }[v] || '')
  1210. },
  1211. 'страницы': {
  1212. key: 'pages',
  1213. value: v => v.replace('-', '—'),
  1214. },
  1215. 'том': {
  1216. key: 'volume'
  1217. },
  1218. 'номер': {
  1219. key: 'number'
  1220. },
  1221. 'год': {
  1222. key: 'year'
  1223. },
  1224. 'журнал': {
  1225. key: 'journal'
  1226. },
  1227. };
  1228.  
  1229. var add = function(key, value) {
  1230. var m = mapping[key];
  1231. if (!m) {
  1232. data[key] = value;
  1233. } else {
  1234. data[m.key || key] = (m.value && m.value(value)) || value;
  1235. }
  1236. }
  1237.  
  1238. var processRecords = function(e) {
  1239. if (e.nodeType == 3) {
  1240. var text = $(e).text().trim();
  1241. if (text && text[text.length - 1] == ':')
  1242. processRecords.key = text.substr(0, text.length - 1).toLowerCase();
  1243. } else if ($(e).is('font,a') && processRecords.key !== undefined) {
  1244. var value = $(e).text().trim();
  1245. add(processRecords.key, value);
  1246. processRecords.key = undefined;
  1247. }
  1248. }
  1249.  
  1250. var title = $('tr[valign=middle][align=center] .bigtext').eq(0).parents('table').eq(0);
  1251. var tables = title.nextAll('table');
  1252.  
  1253. data.title = title.text().trim();
  1254. data.author = authorsToWiki(tables.eq(0).find('span b').get().map(e => $(e).text()));
  1255.  
  1256. tables.eq(1).find('table td').contents().each((i, e) => processRecords(e));
  1257.  
  1258. processRecords(tables.eq(2).find('tr font').contents()[0]);
  1259. tables.eq(2).find('tr').eq(1).find('td').eq(1).contents().each((i, e) => processRecords(e));
  1260.  
  1261. var bibtex = clone(bibtexBase);
  1262. bibtex.автор = { selector: 'author' };
  1263. bibtex.язык = { selector: 'язык' };
  1264. bibtex.ссылка = { const: location.href };
  1265.  
  1266. return getWpFromObj(templates.article, bibtex, data);
  1267. },
  1268. },
  1269. ]);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址