wiki-reference

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

目前為 2015-08-20 提交的版本,檢視 最新版本

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

QingJ © 2025

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