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

QingJ © 2025

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