wiki-reference

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

当前为 2015-08-29 提交的版本,查看 最新版本

  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
  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.token === '"')
  375. break;
  376. else if (_parser.token[0] === '\\')
  377. res += entity();
  378. else
  379. res += plainText();
  380. }
  381. return res;
  382. }
  383.  
  384. var field = function() {
  385. var name = fieldValue();
  386. _parser.match("=");
  387. var value = fieldValue();
  388.  
  389. return {
  390. name: name,
  391. value: value
  392. }
  393. }
  394.  
  395. this.index = function() {
  396. return _parser.index();
  397. }
  398.  
  399. this.parse = function(entryTypes) {
  400. if (!entryTypes)
  401. entryTypes = []
  402. else if (typeof entryTypes == 'string' || entryTypes instanceof String)
  403. entryTypes = [ entryTypes ];
  404. var realType = type(entryTypes);
  405.  
  406. if (realType.toLowerCase() == 'comment')
  407. var result = comment();
  408. else
  409. var result = entry();
  410.  
  411. result.type = realType;
  412. return result;
  413. }
  414.  
  415. this.parseAmsFieldValue = function() {
  416. return amsFieldValue();
  417. }
  418. }
  419.  
  420. var bibtexBase = {
  421. 'автор': {
  422. selector: 'author',
  423. map: (text) => authorsToWiki(getAuthors(text)),
  424. },
  425. 'заглавие': {
  426. selector: 'title',
  427. map: function(text) {
  428. return text.replace(/^"|"$/g, '')
  429. }
  430. },
  431. 'издание': {
  432. selector: 'journal',
  433. map: function(text) {
  434. return {
  435. aj: 'Astronomical Journal',
  436. actaa: 'Acta Astronomica',
  437. araa: 'Annual Review of Astron and Astrophys',
  438. apj: 'Astrophysical Journal',
  439. apjl: 'Astrophysical Journal, Letters',
  440. apjs: 'Astrophysical Journal, Supplement',
  441. ao: 'Applied Optics',
  442. apss: 'Astrophysics and Space Science',
  443. aap: 'Astronomy and Astrophysics',
  444. aapr: 'Astronomy and Astrophysics Reviews',
  445. aaps: 'Astronomy and Astrophysics, Supplement',
  446. azh: 'Astronomicheskii Zhurnal',
  447. baas: 'Bulletin of the AAS',
  448. caa: 'Chinese Astronomy and Astrophysics',
  449. cjaa: 'Chinese Journal of Astronomy and Astrophysics',
  450. icarus: 'Icarus',
  451. jcap: 'Journal of Cosmology and Astroparticle Physics',
  452. jrasc: 'Journal of the RAS of Canada',
  453. memras: 'Memoirs of the RAS',
  454. mnras: 'Monthly Notices of the RAS',
  455. na: 'New Astronomy',
  456. nar: 'New Astronomy Review',
  457. pra: 'Physical Review A: General Physics',
  458. prb: 'Physical Review B: Solid State',
  459. prc: 'Physical Review C',
  460. prd: 'Physical Review D',
  461. pre: 'Physical Review E',
  462. prl: 'Physical Review Letters',
  463. pasa: 'Publications of the Astron. Soc. of Australia',
  464. pasp: 'Publications of the ASP',
  465. pasj: 'Publications of the ASJ',
  466. rmxaa: 'Revista Mexicana de Astronomia y Astrofisica',
  467. qjras: 'Quarterly Journal of the RAS',
  468. skytel: 'Sky and Telescope',
  469. solphys: 'Solar Physics',
  470. sovast: 'Soviet Astronomy',
  471. ssr: 'Space Science Reviews',
  472. zap: 'Zeitschrift fuer Astrophysik',
  473. nat: 'Nature',
  474. iaucirc: 'IAU Cirulars',
  475. aplett: 'Astrophysics Letters',
  476. apspr: 'Astrophysics Space Physics Research',
  477. bain: 'Bulletin Astronomical Institute of the Netherlands',
  478. fcp: 'Fundamental Cosmic Physics',
  479. gca: 'Geochimica Cosmochimica Acta',
  480. grl: 'Geophysics Research Letters',
  481. jcp: 'Journal of Chemical Physics',
  482. jgr: 'Journal of Geophysics Research',
  483. jqsrt: 'Journal of Quantitiative Spectroscopy and Radiative Transfer',
  484. memsai: 'Mem. Societa Astronomica Italiana',
  485. nphysa: 'Nuclear Physics A',
  486. physrep: 'Physics Reports',
  487. physscr: 'Physica Scripta',
  488. planss: 'Planetary Space Science',
  489. procspie: 'Proceedings of the SPIE',
  490. }[text] || text;
  491. },
  492. },
  493. 'год': {
  494. selector: 'year',
  495. },
  496. 'выпуск': {
  497. selector: 'number',
  498. },
  499. 'том': {
  500. selector: 'volume',
  501. },
  502. 'страницы': {
  503. selector: 'pages',
  504. },
  505. 'издательство': {
  506. selector: 'publisher',
  507. },
  508. 'issn': {
  509. selector: 'issn',
  510. },
  511. 'doi': {
  512. selector: 'doi',
  513. },
  514. 'arxiv': {
  515. selector: 'eprint',
  516. map: function(text) {
  517. const prefix = 'arXiv:';
  518. if (text.indexOf(prefix) == 0)
  519. text = text.substr(prefix.length);
  520. return text;
  521. }
  522. },
  523. 'ссылка': { const: "" },
  524. 'язык': { const: "en" },
  525. };
  526.  
  527. var getAuthors = function(text) {
  528. return text.split(' and ').map(function(name) {
  529. return name.replace(',', '');
  530. });
  531. };
  532.  
  533. var authorsToWiki = function(authors) {
  534. return authors.map(function(name) {
  535. return new Template('nobr').add(name).toWiki()
  536. }).join(', ');
  537. };
  538.  
  539. var switchAuthorNameParts = function(name) {
  540. var match = /^(.+) (\S+)$/.exec(name);
  541. if (!match) return name;
  542. return match[2] + ' ' + match[1];
  543. }
  544.  
  545. var getWpFromObj = function(name, rules, obj) {
  546. var article = new Template(name);
  547.  
  548. for(var name in rules) {
  549. var rule = rules[name];
  550.  
  551. var value;
  552. if (rule.const !== undefined)
  553. value = rule.const
  554. else {
  555. if (typeof rule.selector === "function")
  556. value = rule.selector(obj);
  557. else
  558. value = obj[rule.selector];
  559. if (!value)continue;
  560. }
  561.  
  562. if (rule.map)
  563. value = rule.map(value, obj);
  564.  
  565. article.add(name, value);
  566. }
  567.  
  568. return getRef(article);
  569. }
  570.  
  571. var getRef = function(template) {
  572. // adding additional args
  573. for(var a of templateAdditionalArgs[template.getName()] || []) {
  574. if (template.get(a) === undefined)
  575. template.add(a, '');
  576. }
  577.  
  578. // ordering
  579. var orderedTemplate = new Template(template.getName());
  580. var names = template.getParamNames();
  581. var getOrder = x => {
  582. var i = refTemplateParamOrder.indexOf(x.toLowerCase());
  583. return i === -1 ? 99999 : i;
  584. }
  585. names.sort((x, y) => getOrder(x) - getOrder(y));
  586.  
  587. for(var name of names)
  588. orderedTemplate.add(name, template.get(name));
  589.  
  590. return orderedTemplate.toWiki();
  591. }
  592.  
  593. var testUrl = function(regex) {
  594. return regex.exec(window.location.href)
  595. }
  596.  
  597. var createWpButton = function(getResult, tag, param) {
  598. tag = tag || 'a';
  599. return $('<' + tag + '>')
  600. .attr('href', '#')
  601. .text('WP')
  602. .click(function(e) {
  603. e.preventDefault();
  604.  
  605. var promise = $.Deferred();
  606.  
  607. promise.done(function(result) {
  608. showResult(result);
  609. }).fail(function(result) {
  610. alert(result);
  611. });
  612.  
  613. getResult({
  614. resolve: function(result) {
  615. promise.resolve(result)
  616. },
  617. reject: function(result) {
  618. promise.reject(result)
  619. },
  620. }, param);
  621. })
  622. .get(0);
  623. }
  624.  
  625. var showResult = function(text) {
  626. var button;
  627.  
  628. var dialog = $('<div>').css({
  629. 'position': 'fixed',
  630. 'top': 0,
  631. 'left': 0,
  632. 'width': '100%',
  633. 'height': '100%',
  634. 'z-index': 9999999,
  635. }).appendTo(document.body).append(
  636. // dimmer
  637. $('<div>').css({
  638. 'width': '100%',
  639. 'height': '100%',
  640. 'background-color': 'black',
  641. 'opacity': 0.6,
  642. })
  643. ).append(
  644. // dialog container
  645. $('<div>').css({
  646. 'display': 'table',
  647. 'position': 'absolute',
  648. 'top': 0,
  649. 'left': 0,
  650. 'width': '100%',
  651. 'height': '100%',
  652. 'font': '12px sans-serif',
  653. }).append(
  654. $('<div>').css({
  655. 'text-align': 'center',
  656. 'display': 'table-cell',
  657. 'vertical-align': 'middle',
  658. }).append(
  659. // dialog
  660. $('<div>').css({
  661. 'padding': 10,
  662. 'background-color': 'white',
  663. 'display': 'inline-block',
  664. 'max-width': '80%',
  665. }).append(
  666. // text
  667. $('<div>').css({
  668. 'padding': 5,
  669. 'white-space': 'pre-wrap',
  670. 'text-align': 'left',
  671. }).text(text)
  672. ).append(
  673. // buttons
  674. $('<div>').css({
  675. 'text-align': 'right',
  676. 'margin-top': 10,
  677. }).append(
  678. button = $('<button>').text('Copy & close').click(function() {
  679. GM_setClipboard(text);
  680. dialog.remove();
  681. }).focus()
  682. ).append(
  683. $('<button>').text('Cancel').click(function() {
  684. dialog.remove();
  685. })
  686. )
  687. )
  688. )
  689. )
  690. );
  691.  
  692. button.focus();
  693. };
  694.  
  695. var getFormData = function(data) {
  696. if (data && typeof(data) !== 'string') {
  697. var params = [];
  698. for(var k in data) {
  699. params.push(encodeURIComponent(k) + '=' + encodeURIComponent(data[k]))
  700. }
  701. data = params.join('&');
  702. }
  703. return data;
  704. }
  705.  
  706. var ajaxGet = function(url, process) {
  707. var data;
  708. if (!(typeof url === 'string' || url instanceof String)) {
  709. var { url, data } = url;
  710. }
  711.  
  712. if (data)
  713. url = url + '?' + getFormData(data);
  714.  
  715. return ajax({
  716. method: 'GET',
  717. url: url,
  718. }, process);
  719. };
  720.  
  721. var ajaxPost = function({ url, data }, process) {
  722. return ajax({
  723. method: 'POST',
  724. url: url,
  725. data: getFormData(data),
  726. headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
  727. }, process);
  728. };
  729.  
  730. var ajax = function(params, process) {
  731. var promise = $.Deferred();
  732.  
  733. params.onload = function(response) {
  734. try {
  735. promise.resolve(process(response.responseText));
  736. } catch(e) {
  737. promise.reject(e + '\n\n' + e.stack);
  738. }
  739. }
  740. setTimeout(function() {
  741. GM_xmlhttpRequest(params)
  742. }, 0);
  743.  
  744. return promise.promise();
  745. };
  746.  
  747. var getBibtex = function(params) {
  748. return function(text) {
  749. var bibtex = (params.parse || (t => new Bibtex(t).parse()))(text);
  750. var template = (params.getTemplate && params.getTemplate(bibtex)) || params.templates[bibtex.type.toLowerCase()];
  751. if (!template) {
  752. throw new Error('unknown type: ' + bibtex.type);
  753. }
  754. return getWpFromObj(template, params.getRules(bibtex.type), bibtex);
  755. }
  756. };
  757.  
  758. var getXml = function(params) {
  759. return function(text) {
  760. var xml = params.parse(text);
  761. var template = params.getTemplate(xml);
  762. if (!template) {
  763. throw new Error('unknown type: ' + bibtex.type);
  764. }
  765. return getWpFromXml(template, params.getRules(xml), xml);
  766. }
  767. };
  768.  
  769. var proecssPage = function(page, res) {
  770. var onButton = function(promise, param) {
  771. try {
  772. $.when(page.act(res, param)).then(promise.resolve, promise.reject);
  773. } catch(e) {
  774. promise.reject(e + '\n\n' + e.stack);
  775. }
  776. };
  777.  
  778. page.attach(createWpButton.bind(null, onButton));
  779. };
  780.  
  781. (function(pages) {
  782. try {
  783. for(var page of pages) {
  784. var res = page.test();
  785. if (res) {
  786. proecssPage(page, res);
  787. break;
  788. }
  789. };
  790. } catch(e) {
  791. alert(e + '\n\n' + e.stack);
  792. }
  793. })([
  794. // pubmed
  795. {
  796. test: function() {
  797. return testUrl(/\/pubmed\/(\d+)$/);
  798. },
  799. attach: function(createButton) {
  800. $('#messagearea').after(
  801. $('<div>').append(createButton())
  802. );
  803. },
  804. act: ([,id]) => ajaxGet({
  805. url: id,
  806. data: { dopt: 'Abstract', report: 'xml', format: 'text' },
  807. }, getXml({
  808. parse: function(text) {
  809. var html = new DOMParser().parseFromString(text, "text/html");
  810. return new DOMParser().parseFromString(html.body.textContent, "text/xml");
  811. },
  812. getTemplate: () => templates.article,
  813. getRules: () => ({
  814. 'автор': {
  815. selector: 'Article > AuthorList > Author',
  816. map: function(node) { return [ node.querySelector('LastName'), node.querySelector('Initials') ] },
  817. mapText: function(nodes) { return new Template('nobr').add(nodes[0] + ' ' + nodes[1].replace(/(.)/g, "$1. ").trim()).toWiki() },
  818. },
  819. 'заглавие': {
  820. selector: 'Article > ArticleTitle',
  821. mapText: function(text) { return text.replace(/^(.*?)\.?$/, "$1") },
  822. },
  823. 'издание': {
  824. selector: 'Article > Journal > Title',
  825. },
  826. 'год': {
  827. selector: 'Article > Journal > JournalIssue > PubDate',
  828. mapText: function(text) { return /^\s*(\d{4})/.exec(text)[1] },
  829. },
  830. 'выпуск': {
  831. selector: 'Article > Journal > JournalIssue > Issue',
  832. },
  833. 'том': {
  834. selector: 'Article > Journal > JournalIssue > Volume',
  835. },
  836. 'страницы': {
  837. selector: 'Article > Pagination > MedlinePgn',
  838. mapText: text => text.replace('-', '—'),
  839. },
  840. 'issn': {
  841. selector: 'Article > Journal > ISSN[IssnType=Electronic]',
  842. },
  843. 'doi': {
  844. selector: 'ArticleIdList > ArticleId[IdType=doi]',
  845. },
  846. 'pmid': {
  847. selector: 'ArticleIdList > ArticleId[IdType=pubmed]',
  848. },
  849. 'ссылка': { const: "" },
  850. }),
  851. })),
  852. },
  853. // adsabs
  854. {
  855. test: function() {
  856. return testUrl(/adsabs\.harvard\.edu\/(?:abs|doi)\/(.*)$/);
  857. },
  858. attach: function(createButton) {
  859. $('h3').eq(0)
  860. .append(document.createTextNode(' '))
  861. .append(createButton());
  862. },
  863. act: ([,id]) => ajaxGet({
  864. url: '/cgi-bin/nph-bib_query',
  865. data: { data_type: 'BIBTEX', bibcode: id },
  866. }, getBibtex({
  867. parse: function(text) {
  868. var index = text.indexOf('@ARTICLE{');
  869. if (index == -1)
  870. throw new Error('bibtex not found');
  871. return new Bibtex(text, index).parse('article');
  872. },
  873. templates: {
  874. 'article': templates.article,
  875. },
  876. getRules: function() {
  877. var bibtex = clone(bibtexBase);
  878. bibtex.bibcode = { selector: 'bibcode' };
  879. return bibtex;
  880. },
  881. })),
  882. },
  883. // ufn
  884. {
  885. test: function() {
  886. return testUrl(/\/ufn\.ru\/ru\/articles\/(\d+\/\d+\/\w+)\//);
  887. },
  888. attach: function(createButton) {
  889. $('#print > table tr > td').next().append(
  890. $('<td>').append(createButton())
  891. );
  892. },
  893. act: ([, id]) => ajaxGet('/ru/articles/' + id + '/citation/ru/bibtex.html', getBibtex({
  894. parse: function(text) {
  895. var html = new DOMParser().parseFromString(text, "text/html");
  896. var node = html.body.querySelector('.cit_code > pre');
  897. if (!node)
  898. throw new Error('bibtex not found');
  899. return new Bibtex(node.textContent).parse('article');
  900. },
  901. templates: {
  902. 'article': templates.article,
  903. },
  904. getRules: function() {
  905. var bibtex = clone(bibtexBase);
  906.  
  907. var nameRegex = /^(.+) (\S+)$/;
  908. bibtex.автор.map = function(text) {
  909. return authorsToWiki(getAuthors(text).map(switchAuthorNameParts));
  910. }
  911. delete bibtex.издательство;
  912. bibtex.страницы.map = t => t.replace('-', '—');
  913. bibtex.ссылка = { const: 'http://ufn.ru/ru/articles/' + id + '/' };
  914. bibtex.язык = { const: "ru" };
  915.  
  916. return bibtex;
  917. }
  918. })),
  919. },
  920. // google books
  921. {
  922. test: function() {
  923. return window.self == window.top && testUrl(/https?:\/\/books\.google\..+\/books.*[?&]id=([^&$]+)/);
  924. },
  925. attach: function(createButton) {
  926. var button = $(createButton()).addClass('gb-button').css('margin-left', '4px');
  927. $('.metadata_value > .gb-button:last-child, #gb-get-book-container > *:first-child')
  928. .parent()
  929. .append(button);
  930. },
  931. act: ([, id]) => ajaxGet({
  932. url: 'http://books.google.us/books/download/',
  933. data: { id: id, output: 'bibtex' },
  934. }, getBibtex({
  935. parse: t => new Bibtex(t).parse('book'),
  936. templates: {
  937. 'book': templates.book
  938. },
  939. getRules: function() {
  940. var bibtex = clone(bibtexBase);
  941. bibtex.серия = { 'selector': 'series' };
  942. bibtex.страниц = { const: "" };
  943. bibtex.isbn = { 'selector': 'isbn' };
  944. bibtex.ссылка = { const: 'http://books.google.com/books?id=' + id };
  945. return bibtex;
  946. }
  947. })),
  948. },
  949. // sciencedirect
  950. {
  951. test: function() {
  952. return testUrl(/sciencedirect\.com\/science\/article\//);
  953. },
  954. attach: function(createButton) {
  955. $('#articleNav > ul').prepend($('<li />').append(
  956. $(createButton()).attr('type', 'button').val('WP').css({
  957. 'vertical-align': 'middle',
  958. 'line-height': '40px',
  959. })
  960. ));
  961. },
  962. act: function() {
  963. var params = {
  964. 'citation-type': 'BIBTEX'
  965. };
  966. $('form[name=exportCite] input[type=hidden]').each(function(i, hidden) {
  967. params[$(hidden).attr('name')] = $(hidden).val();
  968. });
  969. return ajaxPost({
  970. url: $('form[name=exportCite]').attr('action'),
  971. data: params,
  972. }, getBibtex({
  973. parse: t => new Bibtex(t).parse('article'),
  974. templates: {
  975. 'article': templates.article
  976. },
  977. getRules: function() {
  978. var bibtex = clone(bibtexBase);
  979. bibtex.ссылка = { selector: 'url' };
  980. bibtex.doi.map = function(text) {
  981. var res = /http:\/\/dx\.doi\.org\/(.*)$/.exec(text) || [];
  982. return res[1] || text;
  983. }
  984. bibtex.страницы.map = function(text) {
  985. return text.replace(' - ', '—')
  986. }
  987. return bibtex;
  988. }
  989. }))
  990. },
  991. },
  992. // Library Genesis
  993. {
  994. test: function() {
  995. return testUrl(/gen\.lib\.rus\.ec\/scimag\//);
  996. },
  997. attach: function(createButton) {
  998. var node = document.querySelector('form[name=search]');
  999. while(node && node.nodeName != 'TABLE') {
  1000. node = node.nextSibling;
  1001. }
  1002. var rows = node.querySelectorAll('tr');
  1003. for(var i = 1; i < rows.length; i++) {
  1004. let anchor = rows[i].querySelector('td:first-child > b');
  1005. if (!anchor)
  1006. continue;
  1007. var button = createButton('a', anchor.textContent);
  1008. rows[i].querySelector('td:last-child').appendChild(button);
  1009. button.style.fontWeight = 'bold';
  1010. }
  1011. },
  1012. act: (_, doi) => ajaxGet({
  1013. url: 'http://gen.lib.rus.ec/scimag/bibtex.php',
  1014. data: { doi: doi },
  1015. }, getBibtex({
  1016. parse: text => {
  1017. var html = new DOMParser().parseFromString(text, "text/html");
  1018. return new Bibtex(html.querySelector('textarea#bibtext').value).parse('article');
  1019. },
  1020. templates: {
  1021. 'article': templates.article
  1022. },
  1023. getRules: function() {
  1024. var bibtex = clone(bibtexBase);
  1025. bibtex.выпуск = { selector: 'issue' };
  1026. bibtex.страницы = { selector: 'page' };
  1027. bibtex.язык = { const: '' };
  1028. return bibtex;
  1029. },
  1030. })),
  1031. },
  1032. // wiley
  1033. {
  1034. test: function() {
  1035. return testUrl(/onlinelibrary\.wiley\.com\/doi\//)
  1036. },
  1037. attach: function(createButton) {
  1038. $('#promosAndTools .titleTools').append($('<li>').append(createButton()));
  1039. },
  1040. act: function() {
  1041. var [, doi] = /^DOI:\s+(.+)$/.exec($('#doi').text().trim());
  1042. if (!doi) throw new Error('doi not found');
  1043.  
  1044. return ajaxPost({
  1045. url: '/documentcitationdownloadformsubmit',
  1046. data: { doi: doi, fileFormat: 'BIBTEX', hasAbstract: 'CITATION' },
  1047. }, getBibtex({
  1048. templates: {
  1049. 'article': templates.article,
  1050. },
  1051. getRules: function() {
  1052. var bibtex = clone(bibtexBase);
  1053. bibtex.ссылка = { selector: 'url' };
  1054. return bibtex;
  1055. }
  1056. }))
  1057. },
  1058. },
  1059. // jstor
  1060. {
  1061. test: function() {
  1062. return testUrl(/www\.jstor\.org\/[^\/]+\/(.*?)($|\?)/)
  1063. },
  1064. attach: function(createButton) {
  1065. $('#citation-tools-drop').append($('<li>').append(createButton()));
  1066. },
  1067. act: () => ajaxPost({
  1068. url: '/action/downloadSingleCitationSec?userAction=export&format=bibtex&include=abs&singleCitation=true',
  1069. data: { noDoi: 'yesDoi', doi: /DOI:\s*(.*)/.exec($('.doi').text().trim())[1] },
  1070. }, getBibtex({
  1071. parse: txt => {
  1072. txt = txt.trim();
  1073. const header = 'JSTOR CITATION LIST';
  1074.  
  1075. var index = txt.indexOf(header);
  1076. if (index == -1)
  1077. throw new Error('header not found');
  1078. index += header.length;
  1079.  
  1080. while(index < txt.length) {
  1081. var bibtex = new Bibtex(txt, index);
  1082. var result = bibtex.parse([ 'article', 'comment', 'book' ]);
  1083. if (result.type != 'comment')
  1084. return result;
  1085. index = bibtex.index();
  1086. }
  1087. throw new Error('bibtex not found');
  1088. },
  1089. templates: {
  1090. 'article': templates.article,
  1091. 'book': templates.book,
  1092. },
  1093. getRules: function() {
  1094. var bibtex = clone(bibtexBase);
  1095. bibtex.ссылка = { selector: 'url' };
  1096. bibtex.выпуск.selector = function(obj) {
  1097. return obj['number'] || obj['jstor_issuetitle'];
  1098. }
  1099. bibtex.страницы.map = function(text) {
  1100. return text.replace(/^p?p\. /, "").replace('-', '—');
  1101. };
  1102. return bibtex;
  1103. },
  1104. })),
  1105. },
  1106. // projecteuclid
  1107. {
  1108. test: function() {
  1109. return testUrl(/projecteuclid.org\/(euclid\..*?\/\d+)/);
  1110. },
  1111. attach: function(createButton) {
  1112. $('#export-form').append($(createButton()).addClass('btn export-link-special'))
  1113. },
  1114. act: ([, id]) => ajaxPost({
  1115. url: '/export_citations',
  1116. data: { format: "bibtex", delivery: "browser", address: '', h: id },
  1117. }, getBibtex({
  1118. templates: {
  1119. 'article': templates.article,
  1120. 'inbook': templates.book,
  1121. },
  1122. getRules: function(type) {
  1123. var bibtex = clone(bibtexBase);
  1124. bibtex.издание = { selector: 'fjournal' };
  1125.  
  1126. if (type.toLowerCase() == 'inbook') {
  1127. bibtex.место = { selector: 'address' };
  1128. bibtex.часть = bibtex.заглавие;
  1129. bibtex.заглавие = { selector: 'booktitle' };
  1130. bibtex['ссылка часть'] = { selector: 'url' };
  1131. delete bibtex.ссылка;
  1132. } else {
  1133. bibtex.ссылка = { selector: 'url' };
  1134. }
  1135.  
  1136. return bibtex;
  1137. }
  1138. })),
  1139. },
  1140. // springer
  1141. {
  1142. test: () => testUrl(/\/link\.springer\.com\/([^#]+)/),
  1143. attach: function(createButton) {
  1144. if ($('#export-citation').length)
  1145. $('.other-actions > ul').append($('<li>').append(createButton()));
  1146. },
  1147. act: ([, id]) => ajaxGet('http://link.springer.com/export-citation/' + id + '.bib', getBibtex({
  1148. parse: text => new Bibtex(text.replace(/^@.*?{/, '$&x,')).parse(),
  1149. templates: {
  1150. 'article': templates.article,
  1151. 'incollection': templates.book,
  1152. },
  1153. getRules: function(type) {
  1154. var bibtex = clone(bibtexBase);
  1155. bibtex.страницы.map = t => t.replace('-', '—');
  1156. bibtex.ссылка = { const: 'http://link.springer.com/' + id };
  1157. if (type == 'incollection') {
  1158. bibtex.ответственный = {
  1159. selector: 'editor',
  1160. map: text => 'Ed. by ' + authorsToWiki(getAuthors(text)),
  1161. };
  1162. bibtex.серия = { 'selector': 'series' };
  1163. bibtex.часть = bibtex.заглавие;
  1164. bibtex.заглавие = { selector: 'booktitle' };
  1165. bibtex['ссылка часть'] = bibtex.ссылка;
  1166. bibtex.ссылка = { const: $('#about-link').attr('href') };
  1167. bibtex.isbn = { 'selector': 'isbn' };
  1168. } else if (type == 'article') {
  1169. delete bibtex.издательство;
  1170. }
  1171. return bibtex;
  1172. }
  1173. })),
  1174. },
  1175. // mathnet
  1176. {
  1177. test: () => testUrl(/\/www\.mathnet\.ru\//),
  1178. attach: function(createButton) {
  1179. $('#citPaperAMSBIBID').before(createButton());
  1180. },
  1181. act: () => getBibtex({
  1182. parse: t => {
  1183. var transl = false;
  1184. var res = {};
  1185. for(var line of t.split('\n')) {
  1186. if (!line) continue;
  1187. var [, key, value] = /^\\(\w+)\s*(.*)$/.exec(line);
  1188. if (key === 'transl' || key === 'rtransl') {
  1189. transl = true;
  1190. continue;
  1191. }
  1192. if (transl)
  1193. key = 'transl_' + key;
  1194. if (key == 'by')
  1195. value = value.replace(/([^\\]), /g, '$1 and ');
  1196. res[key] = new Bibtex(value).parseAmsFieldValue();
  1197. }
  1198. return res;
  1199. },
  1200. getTemplate: () => templates.article,
  1201. getRules: () => ({
  1202. 'автор': {
  1203. selector: 'by',
  1204. map: text => authorsToWiki(getAuthors(text).map(switchAuthorNameParts)),
  1205. },
  1206. 'заглавие': { selector: 'paper' },
  1207. 'издание': { selector: b => b.journalname || b.jour },
  1208. 'год': { selector: 'yr' },
  1209. 'выпуск': { selector: 'issue' },
  1210. 'том': { selector: 'vol' },
  1211. 'страницы': { selector: 'pages' },
  1212. 'издательство': { selector: 'publ' },
  1213. 'ссылка': { selector: 'mathnet' },
  1214. 'язык': { const: 'ru' },
  1215. 'bibcode': {
  1216. selector: 'adsnasa',
  1217. map: url => /bib_query\?(.*)$/.exec(url)[1],
  1218. }
  1219. }),
  1220. })($('#citPaperAMSBIBID pre').text())
  1221. },
  1222. // elibrary
  1223. {
  1224. test: () => testUrl(/elibrary\.ru\/item.asp/),
  1225. attach: function(createButton) {
  1226. var space = $('.left-panel').eq(0).parents('tr').eq(0).next('tr');
  1227. $('<tr>').append(
  1228. $('<td>').append(
  1229. $('<div>').addClass('left-panel').append(
  1230. $(createButton()).css({
  1231. fontWeight: 'bold',
  1232. color: '#F26C4F',
  1233. })
  1234. )
  1235. )
  1236. ).insertAfter(space).after(space.clone());
  1237. },
  1238. act: () => {
  1239. var data = {};
  1240. const mapping = {
  1241. 'язык': {
  1242. value: v => ({
  1243. 'русский': 'ru',
  1244. 'английский': 'en',
  1245. }[v] || '')
  1246. },
  1247. 'страницы': {
  1248. key: 'pages',
  1249. value: v => v.replace('-', '—'),
  1250. },
  1251. 'том': {
  1252. key: 'volume'
  1253. },
  1254. 'номер': {
  1255. key: 'issue'
  1256. },
  1257. 'год': {
  1258. key: 'year'
  1259. },
  1260. 'журнал': {
  1261. key: 'journal'
  1262. },
  1263. };
  1264.  
  1265. var add = function(key, value) {
  1266. var m = mapping[key];
  1267. if (!m) {
  1268. data[key] = value;
  1269. } else {
  1270. data[m.key || key] = (m.value && m.value(value)) || value;
  1271. }
  1272. }
  1273.  
  1274. var processRecords = function(e) {
  1275. console.log(e);
  1276. if (e.nodeType == 3) {
  1277. var text = $(e).text().trim();
  1278. if (text && text[text.length - 1] == ':')
  1279. processRecords.key = text.substr(0, text.length - 1).toLowerCase();
  1280. } else if ($(e).is('font,a') && processRecords.key !== undefined) {
  1281. var value = $(e).text().trim();
  1282. add(processRecords.key, value);
  1283. processRecords.key = undefined;
  1284. }
  1285. }
  1286.  
  1287. var title = $('tr[valign=middle][align=center] .bigtext').eq(0).parents('table').eq(0);
  1288. var tables = title.nextAll('table');
  1289.  
  1290. data.title = title.text().trim();
  1291. data.author = authorsToWiki(tables.eq(0).find('span b').get().map(e => $(e).text()));
  1292.  
  1293. tables.eq(1).find('table td').contents().each((i, e) => processRecords(e));
  1294.  
  1295. processRecords(tables.eq(2).find('tr font').contents()[0]);
  1296. tables.eq(2).find('tr').eq(1).find('td').eq(1).contents().each((i, e) => processRecords(e));
  1297.  
  1298. console.log(data);
  1299.  
  1300. var bibtex = clone(bibtexBase);
  1301. bibtex.автор = { selector: 'author' };
  1302. bibtex.язык = { selector: 'язык' };
  1303. bibtex.ссылка = { const: location.href };
  1304.  
  1305. return getWpFromObj(templates.article, bibtex, data);
  1306. },
  1307. },
  1308. ]);

QingJ © 2025

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