// ==UserScript== // @name wiki-reference // @namespace torvin // @include https://www.ncbi.nlm.nih.gov/pubmed/* // @include http://www.ncbi.nlm.nih.gov/pubmed/* // @include http://adsabs.harvard.edu/abs/* // @include http://adsabs.harvard.edu/doi/* // @include http://ufn.ru/ru/articles/* // @include http://books.google.*/books* // @include https://books.google.*/books* // @include http://www.sciencedirect.com/science/article/* // @include http://gen.lib.rus.ec/scimag/* // @include http://onlinelibrary.wiley.com/doi/* // @include http://www.jstor.org/stable/* // @include http://www.jstor.org/discover/* // @include http://projecteuclid.org/euclid.* // @include https://projecteuclid.org/euclid.* // @include http://link.springer.com/* // @include http://www.mathnet.ru/php/archive.phtml*wshow=paper* // @include http://elibrary.ru/item.asp* // @require https://code.jquery.com/jquery-2.1.1.min.js // @version 1.8.8 // @description Позволяет генерировать ссылки в формате {{статья}} и {{книга}} для ру-вики // @grant GM_xmlhttpRequest // @grant GM_setClipboard // @downloadURL https://update.greasyfork.icu/scripts/1663/wiki-reference.user.js // @updateURL https://update.greasyfork.icu/scripts/1663/wiki-reference.meta.js // ==/UserScript== const templates = { article: 'статья', book: 'книга', }; const templateAdditionalArgs = { 'статья': [ 'ref', 'archiveurl', 'archivedate' ], 'книга': [ 'ref' ], }; const refTemplateParamOrder = [ 'автор', 'часть', 'ссылка часть', 'заглавие', 'ссылка', 'ответственный', 'издание', 'издательство', 'год', 'серия', 'том', 'номер', 'страницы', 'страниц', 'язык', 'doi', 'bibcode', 'arxiv', 'isbn', 'archiveurl', 'archivedate', 'ref' ]; var Template = function(name) { var attrs = []; this.add = function(name, value) { attrs.push({ name: name, value: value }); return this; }; this.get = function(name) { return attrs.filter(a => a.name === name).map(a => a.value)[0]; } this.getParamNames = function() { return attrs.map(a => a.name); } this.getName = function() { return name; } var getAttr = function(x) { return "|" + x.name + (x.value === undefined ? "" : " = " + x.value); }; this.toWiki = function() { if (attrs.length == 1) return "{{" + name + getAttr(attrs[0]) + "}}"; else return "{{" + name + "\n" + attrs.map(a => " " + getAttr(a)).join("\n") + "\n}}"; }; }; var getText = function(node) { return node instanceof Element ? node.textContent : node; }; var clone = function(obj) { var target = {}; for (var i in obj) { var value = obj[i]; if (value instanceof Function) ; else if (typeof value == 'string') ; else value = clone(value); target[i] = value; } return target; } var getWpFromXml = function(name, rules, xml) { var article = new Template(name); for(var name in rules) { var rule = rules[name]; article.add(name, rule.const || Array.slice(xml.querySelectorAll(rule.selector)).map(function(node) { if (rule.map) node = rule.map(node); return Array.isArray(node) ? node.map(getText) : getText(node); }).map(function(item) { return rule.mapText ? rule.mapText(item) : item; }).join(rule.separator || ', ') ); } return getRef(article); }; var Parser = function(sourceText, index, tokens) { var _match; var _isEof; //var _tokenRegex = /(\s*)(\{|\}|,|=|\\\W|\\[\w]+|[^\{\},\\=\s])/g; var _tokenRegex = new RegExp("(\\s*)(" + tokens.map(function(x) { return x.source || x }).join('|') + ")", 'g'); _tokenRegex.lastIndex = index; var getToken = function() { if (_isEof) throw new Error("EOF"); var index = _tokenRegex.lastIndex; var res = _tokenRegex.exec(sourceText); if (!res) { _isEof = true; return null; } _match = { match: res[0], token: res[2], space: res[1].replace(/\s+/g, ' '), index: index, } } this.matchAny = function() { var res = _match; getToken(); return res; } this.match = function(str) { if (_match.token !== str) throw new Error("Parser error at pos " + _tokenRegex.lastIndex + ". Expected '" + str + "', found '" + _match.token + "'."); return this.matchAny(); } this.matchIgnoreCase = function(str) { if (_match.token.toUpperCase() !== str.toUpperCase()) throw new Error("Parser error at pos " + _tokenRegex.lastIndex + ". Expected '" + str + "', found '" + _match.token + "'."); return this.matchAny(); } this.matchAnyIgnoreCase = function(strs) { if (strs.every(function(str) { return _match.token.toUpperCase() !== str.toUpperCase(); })) throw new Error("Parser error at pos " + _tokenRegex.lastIndex + ". Expected any of: '" + strs.join("', '") + "', found '" + _match.token + "'."); return this.matchAny(); } this.matchNot = function(strs) { if (strs.indexOf(_match.token) != -1) throw new Error("Parser error at pos " + _tokenRegex.lastIndex + ". Unexpected '" + _match.token + "'."); return this.matchAny(); } this.index = function() { return _match.index; } this.isEof = () => _isEof; Object.defineProperty(this, "token", { get: function() { return _match.token; }}); getToken(); } var Bibtex = function(sourceText, index) { var _plainText = /[^\{\},\\=\s"\$]+/; const _tokens = [ /\{/, /\}/, /"/, /,/, /=/, /@/, /\$/, /\\\W/, /\\[\w]+/, _plainText, ]; var _parser = new Parser(sourceText, index || 0, _tokens); var entry = function() { _parser.match("{"); var id = fieldValue(); _parser.match(","); var f = fields(); _parser.match("}"); f.bibcode = id; return f; } var comment = function() { _parser.match("{"); var text = fieldValue(); _parser.match("}"); return { comment: text }; } var type = function(entryTypes) { _parser.match('@'); var token = _parser.token; if (entryTypes.length) _parser.matchAnyIgnoreCase(entryTypes); else _parser.matchAny(); return token; } var fields = function() { var res = {}; for(;;) { var f = field(); res[f.name] = f.value; if (_parser.token !== ",") break; _parser.match(","); if (_parser.token === "}") break; } return res; } var quoted = function() { return _parser.match("{").space + quotedValue() + _parser.match("}").space; } var diacritics = { '`': '\u0300', '\'': '\u0301', '^': '\u0302', '~': '\u0303', '=': '\u0304', 'u': '\u0306', '.': '\u0307', '"': '\u0308', 'r': '\u030a', 'H': '\u030b', 'v': '\u030c', 'c': '\u0327', 'k': '\u0328', 'd': '\u0323', 'b': '\u0331', 't': '\u0361', }; var ligatures = { 'L': '\u0141', 'l': '\u0142', 'AA': '\u00c5', 'aa': '\u00e5', 'AE': '\u00c6', 'ae': '\u00e6', 'O': '\u00d8', 'o': '\u00f8', 'OE': '\u0152', 'oe': '\u0153', 'i': '\u0131', 'j': '\u0237', 'ss': '\u00df', }; var entity = function() { if (_parser.token[0] != '\\') throw new Error("Expected entity, found " + _parser.token); var cmd = _parser.matchAny().token.substr(1); var value = ligatures[cmd]; if (value) return value; value = diacritics[cmd]; if (value) return fieldValue() + value; return cmd; } var quotedValue = function() { var res = ""; for(;;) { if (_parser.token === "{") res += quoted(); else if (_parser.token === "}") break; else if (_parser.token[0] === '\\') res += entity(); else res += plainText(); } return res; } var plainText = function() { return _parser.matchAny().match.replace(/---?/g, '—').replace(/~/g, ' '); } var fieldValue = function() { var res = ""; for(;;) { if (_parser.token == "{") res += quoted(); else if (_parser.token == '"') res += doubleQuoted(); else if (_parser.token[0] === '\\') res += entity(); else if (_parser.token === '$') res += math(); else if (_plainText.test(_parser.token)) res += plainText(); else break; } return res.trim(); } var amsFieldValue = function() { var res = ""; for(;;) { if (_parser.isEof()) break; else if (_parser.token == "{") res += quoted(); else if (_parser.token == '"') res += doubleQuoted(); else if (_parser.token[0] === '\\') res += entity(); else if (_parser.token === '$') res += math(); else res += plainText(); } return res.trim(); } var math = function() { var before = _parser.match('$').space; var res = ''; for(;;) { if (_parser.token === '$') break; else res += _parser.matchAny().match; } return before + '' + res + '' + _parser.match('$').space; } var doubleQuoted = function() { return _parser.match('"').space + doubleQuotedValue() + _parser.match('"').space; } var doubleQuotedValue = function() { var res = ""; for(;;) { if (_parser.isEof()) throw new Error("Unexpected EOF."); else if (_parser.token == "{") res += quoted(); else if (_parser.token == '"') break; else if (_parser.token[0] === '\\') res += entity(); else if (_parser.token === '$') res += math(); else res += plainText(); } return res; } var field = function() { var name = fieldValue(); _parser.match("="); var value = fieldValue(); return { name: name, value: value } } this.index = function() { return _parser.index(); } this.parse = function(entryTypes) { if (!entryTypes) entryTypes = [] else if (typeof entryTypes == 'string' || entryTypes instanceof String) entryTypes = [ entryTypes ]; var realType = type(entryTypes); if (realType.toLowerCase() == 'comment') var result = comment(); else var result = entry(); result.type = realType; return result; } this.parseAmsFieldValue = function() { return amsFieldValue(); } } var bibtexBase = { 'автор': { selector: 'author', map: (text) => authorsToWiki(getAuthors(text)), }, 'заглавие': { selector: 'title', map: function(text) { return text.replace(/^"|"$/g, '') } }, 'издание': { selector: 'journal', map: function(text) { return { aj: 'Astronomical Journal', actaa: 'Acta Astronomica', araa: 'Annual Review of Astron and Astrophys', apj: 'Astrophysical Journal', apjl: 'Astrophysical Journal, Letters', apjs: 'Astrophysical Journal, Supplement', ao: 'Applied Optics', apss: 'Astrophysics and Space Science', aap: 'Astronomy and Astrophysics', aapr: 'Astronomy and Astrophysics Reviews', aaps: 'Astronomy and Astrophysics, Supplement', azh: 'Astronomicheskii Zhurnal', baas: 'Bulletin of the AAS', caa: 'Chinese Astronomy and Astrophysics', cjaa: 'Chinese Journal of Astronomy and Astrophysics', icarus: 'Icarus', jcap: 'Journal of Cosmology and Astroparticle Physics', jrasc: 'Journal of the RAS of Canada', memras: 'Memoirs of the RAS', mnras: 'Monthly Notices of the RAS', na: 'New Astronomy', nar: 'New Astronomy Review', pra: 'Physical Review A: General Physics', prb: 'Physical Review B: Solid State', prc: 'Physical Review C', prd: 'Physical Review D', pre: 'Physical Review E', prl: 'Physical Review Letters', pasa: 'Publications of the Astron. Soc. of Australia', pasp: 'Publications of the ASP', pasj: 'Publications of the ASJ', rmxaa: 'Revista Mexicana de Astronomia y Astrofisica', qjras: 'Quarterly Journal of the RAS', skytel: 'Sky and Telescope', solphys: 'Solar Physics', sovast: 'Soviet Astronomy', ssr: 'Space Science Reviews', zap: 'Zeitschrift fuer Astrophysik', nat: 'Nature', iaucirc: 'IAU Cirulars', aplett: 'Astrophysics Letters', apspr: 'Astrophysics Space Physics Research', bain: 'Bulletin Astronomical Institute of the Netherlands', fcp: 'Fundamental Cosmic Physics', gca: 'Geochimica Cosmochimica Acta', grl: 'Geophysics Research Letters', jcp: 'Journal of Chemical Physics', jgr: 'Journal of Geophysics Research', jqsrt: 'Journal of Quantitiative Spectroscopy and Radiative Transfer', memsai: 'Mem. Societa Astronomica Italiana', nphysa: 'Nuclear Physics A', physrep: 'Physics Reports', physscr: 'Physica Scripta', planss: 'Planetary Space Science', procspie: 'Proceedings of the SPIE', }[text] || text; }, }, 'год': { selector: 'year', }, 'номер': { selector: 'number', }, 'том': { selector: 'volume', }, 'страницы': { selector: 'pages', }, 'издательство': { selector: 'publisher', }, 'issn': { selector: 'issn', }, 'doi': { selector: 'doi', }, 'arxiv': { selector: 'eprint', map: function(text) { const prefix = 'arXiv:'; if (text.indexOf(prefix) == 0) text = text.substr(prefix.length); return text; } }, 'ссылка': { const: "" }, 'язык': { const: "en" }, }; var getAuthors = function(text) { return text.split(' and ').map(function(name) { return name.replace(',', ''); }); }; var authorsToWiki = function(authors) { return authors.map(function(name) { return new Template('nobr').add(name).toWiki() }).join(', '); }; var switchAuthorNameParts = function(name) { var match = /^(.+) (\S+)$/.exec(name); if (!match) return name; return match[2] + ' ' + match[1]; } var getWpFromObj = function(name, rules, obj) { var article = new Template(name); for(var name in rules) { var rule = rules[name]; var value; if (rule.const !== undefined) value = rule.const else { if (typeof rule.selector === "function") value = rule.selector(obj); else value = obj[rule.selector]; if (!value)continue; } if (rule.map) value = rule.map(value, obj); article.add(name, value); } return getRef(article); } var getRef = function(template) { // adding additional args for(var a of templateAdditionalArgs[template.getName()] || []) { if (template.get(a) === undefined) template.add(a, ''); } // ordering var orderedTemplate = new Template(template.getName()); var names = template.getParamNames(); var getOrder = x => { var i = refTemplateParamOrder.indexOf(x.toLowerCase()); return i === -1 ? 99999 : i; } names.sort((x, y) => getOrder(x) - getOrder(y)); for(var name of names) orderedTemplate.add(name, template.get(name)); return orderedTemplate.toWiki(); } var testUrl = function(regex) { return regex.exec(window.location.href) } var createWpButton = function(getResult, tag, param) { tag = tag || 'a'; return $('<' + tag + '>') .attr('href', '#') .text('WP') .click(function(e) { e.preventDefault(); var promise = $.Deferred(); promise.done(function(result) { showResult(result); }).fail(function(result) { alert(result); }); getResult({ resolve: function(result) { promise.resolve(result) }, reject: function(result) { promise.reject(result) }, }, param); }) .get(0); } var showResult = function(text) { var button; var dialog = $('
').css({ 'position': 'fixed', 'top': 0, 'left': 0, 'width': '100%', 'height': '100%', 'z-index': 9999999, }).appendTo(document.body).append( // dimmer $('
').css({ 'width': '100%', 'height': '100%', 'background-color': 'black', 'opacity': 0.6, }) ).append( // dialog container $('
').css({ 'display': 'table', 'position': 'absolute', 'top': 0, 'left': 0, 'width': '100%', 'height': '100%', 'font': '12px sans-serif', }).append( $('
').css({ 'text-align': 'center', 'display': 'table-cell', 'vertical-align': 'middle', }).append( // dialog $('
').css({ 'padding': 10, 'background-color': 'white', 'display': 'inline-block', 'max-width': '80%', }).append( // text $('
').css({ 'padding': 5, 'white-space': 'pre-wrap', 'text-align': 'left', }).text(text) ).append( // buttons $('
').css({ 'text-align': 'right', 'margin-top': 10, }).append( button = $('