PageRenderTime 60ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/BibTeX.js

https://gitlab.com/mba811/translators
JavaScript | 3399 lines | 3240 code | 77 blank | 82 comment | 212 complexity | 18e4187ac160bacda49c4654aa42f503 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

  1. {
  2. "translatorID": "9cb70025-a888-4a29-a210-93ec52da40d4",
  3. "label": "BibTeX",
  4. "creator": "Simon Kornblith, Richard Karnesky and Emiliano heyns",
  5. "target": "bib",
  6. "minVersion": "2.1.9",
  7. "maxVersion": "",
  8. "priority": 200,
  9. "configOptions": {
  10. "getCollections": true
  11. },
  12. "displayOptions": {
  13. "exportCharset": "UTF-8",
  14. "exportNotes": true,
  15. "exportFileData": false,
  16. "useJournalAbbreviation": false
  17. },
  18. "inRepository": true,
  19. "translatorType": 3,
  20. "browserSupport": "gcsv",
  21. "lastUpdated": "2014-06-23 03:37:22"
  22. }
  23. function detectImport() {
  24. var maxChars = 1048576; // 1MB
  25. var inComment = false;
  26. var block = "";
  27. var buffer = "";
  28. var chr = "";
  29. var charsRead = 0;
  30. var re = /^\s*@[a-zA-Z]+[\(\{]/;
  31. while((buffer = Zotero.read(4096)) && charsRead < maxChars) {
  32. Zotero.debug("Scanning " + buffer.length + " characters for BibTeX");
  33. charsRead += buffer.length;
  34. for (var i=0; i<buffer.length; i++) {
  35. chr = buffer[i];
  36. if (inComment && chr != "\r" && chr != "\n") {
  37. continue;
  38. }
  39. inComment = false;
  40. if(chr == "%") {
  41. // read until next newline
  42. block = "";
  43. inComment = true;
  44. } else if((chr == "\n" || chr == "\r"
  45. // allow one-line entries
  46. || i == (buffer.length - 1))
  47. && block) {
  48. // check if this is a BibTeX entry
  49. if(re.test(block)) {
  50. return true;
  51. }
  52. block = "";
  53. } else if(" \n\r\t".indexOf(chr) == -1) {
  54. block += chr;
  55. }
  56. }
  57. }
  58. }
  59. //%a = first listed creator surname
  60. //%y = year
  61. //%t = first word of title
  62. var citeKeyFormat = "%a_%t_%y";
  63. var fieldMap = {
  64. address:"place",
  65. chapter:"section",
  66. edition:"edition",
  67. type:"type",
  68. series:"series",
  69. title:"title",
  70. volume:"volume",
  71. copyright:"rights",
  72. isbn:"ISBN",
  73. issn:"ISSN",
  74. shorttitle:"shortTitle",
  75. url:"url",
  76. doi:"DOI",
  77. abstract:"abstractNote",
  78. nationality: "country",
  79. language:"language",
  80. assignee:"assignee"
  81. };
  82. // Import/export in BibTeX
  83. var extraIdentifiers = {
  84. lccn: 'LCCN',
  85. mrnumber: 'MR',
  86. zmnumber: 'Zbl',
  87. pmid: 'PMID',
  88. pmcid: 'PMCID'
  89. //Mostly from Wikipedia citation templates
  90. //asin - Amazon ID
  91. //bibcode/refcode - used in astronomy, but haven't seen any Bib(La)TeX examples
  92. //jfm - Jahrbuch ID, but it seems to be part of Zentralblatt MATH, so Zbl
  93. //oclc
  94. //ol - openlibrary.org ID
  95. //osti
  96. //rfc
  97. //ssrn? http://cyber.law.harvard.edu/cybersecurity/Guidelines_for_adding_Bibliography_entries
  98. };
  99. // Make a reverse map for convenience
  100. var revExtraIds = {};
  101. for(var field in extraIdentifiers) {
  102. revExtraIds[extraIdentifiers[field]] = field;
  103. }
  104. // Import only. Exported by BibLaTeX
  105. var eprintIds = {
  106. // eprinttype: Zotero label
  107. // From BibLaTeX manual
  108. 'arxiv': 'arXiv', // Sorry, but no support for eprintclass yet
  109. 'jstor': 'JSTOR',
  110. 'pubmed': 'PMID',
  111. 'hdl': 'HDL',
  112. 'googlebooks': 'GoogleBooksID'
  113. };
  114. function parseExtraFields(extra) {
  115. var lines = extra.split(/[\r\n]+/);
  116. var fields = [];
  117. for(var i=0; i<lines.length; i++) {
  118. var rec = { raw: lines[i] };
  119. var line = lines[i].trim();
  120. var splitAt = line.indexOf(':');
  121. if(splitAt > 1) {
  122. rec.field = line.substr(0,splitAt).trim();
  123. rec.value = line.substr(splitAt + 1).trim();
  124. }
  125. fields.push(rec);
  126. }
  127. return fields;
  128. }
  129. function extraFieldsToString(extra) {
  130. var str = '';
  131. for(var i=0; i<extra.length; i++) {
  132. if(!extra[i].raw) {
  133. str += '\n' + extra[i].field + ': ' + extra[i].value;
  134. } else {
  135. str += '\n' + extra[i].raw;
  136. }
  137. }
  138. return str.substr(1);
  139. }
  140. var inputFieldMap = {
  141. booktitle :"publicationTitle",
  142. school:"publisher",
  143. institution:"publisher",
  144. publisher:"publisher",
  145. issue:"issue",
  146. location:"place"
  147. };
  148. var zotero2bibtexTypeMap = {
  149. "book":"book",
  150. "bookSection":"incollection",
  151. "journalArticle":"article",
  152. "magazineArticle":"article",
  153. "newspaperArticle":"article",
  154. "thesis":"phdthesis",
  155. "letter":"misc",
  156. "manuscript":"unpublished",
  157. "patent" :"patent",
  158. "interview":"misc",
  159. "film":"misc",
  160. "artwork":"misc",
  161. "webpage":"misc",
  162. "conferencePaper":"inproceedings",
  163. "report":"techreport"
  164. };
  165. var bibtex2zoteroTypeMap = {
  166. "book":"book", // or booklet, proceedings
  167. "inbook":"bookSection",
  168. "incollection":"bookSection",
  169. "article":"journalArticle", // or magazineArticle or newspaperArticle
  170. "patent" :"patent",
  171. "phdthesis":"thesis",
  172. "unpublished":"manuscript",
  173. "inproceedings":"conferencePaper", // check for conference also
  174. "conference":"conferencePaper",
  175. "techreport":"report",
  176. "booklet":"book",
  177. "manual":"book",
  178. "mastersthesis":"thesis",
  179. "misc":"book",
  180. "proceedings":"book",
  181. "online":"webpage"
  182. };
  183. /*
  184. * three-letter month abbreviations. i assume these are the same ones that the
  185. * docs say are defined in some appendix of the LaTeX book. (i don't have the
  186. * LaTeX book.)
  187. */
  188. var months = ["jan", "feb", "mar", "apr", "may", "jun",
  189. "jul", "aug", "sep", "oct", "nov", "dec"];
  190. var jabref = {
  191. format: null,
  192. root: {}
  193. };
  194. var alwaysMap = {
  195. "|":"{\\textbar}",
  196. "<":"{\\textless}",
  197. ">":"{\\textgreater}",
  198. "~":"{\\textasciitilde}",
  199. "^":"{\\textasciicircum}",
  200. "\\":"{\\textbackslash}",
  201. "{" : "\\{",
  202. "}" : "\\}"
  203. };
  204. var strings = {};
  205. var keyRe = /[a-zA-Z0-9\-]/;
  206. var keywordSplitOnSpace = true;
  207. var keywordDelimRe = '\\s*[,;]\\s*';
  208. var keywordDelimReFlags = '';
  209. function setKeywordSplitOnSpace( val ) {
  210. keywordSplitOnSpace = val;
  211. }
  212. function setKeywordDelimRe( val, flags ) {
  213. //expect string, but it could be RegExp
  214. if(typeof(val) != 'string') {
  215. keywordDelimRe = val.toString().slice(1, val.toString().lastIndexOf('/'));
  216. keywordDelimReFlags = val.toString().slice(val.toString().lastIndexOf('/')+1);
  217. } else {
  218. keywordDelimRe = val;
  219. keywordDelimReFlags = flags;
  220. }
  221. }
  222. function processField(item, field, value, rawValue) {
  223. if(Zotero.Utilities.trim(value) == '') return null;
  224. if(fieldMap[field]) {
  225. item[fieldMap[field]] = value;
  226. } else if(inputFieldMap[field]) {
  227. item[inputFieldMap[field]] = value;
  228. } else if(field == "journal") {
  229. if(item.publicationTitle) {
  230. item.journalAbbreviation = value;
  231. } else {
  232. item.publicationTitle = value;
  233. }
  234. } else if(field == "fjournal") {
  235. if(item.publicationTitle) {
  236. // move publicationTitle to abbreviation, since itprobably came from 'journal'
  237. item.journalAbbreviation = item.publicationTitle;
  238. }
  239. item.publicationTitle = value;
  240. } else if(field == "author" || field == "editor" || field == "translator") {
  241. // parse authors/editors/translators
  242. var names = splitUnprotected(rawValue.trim(), /\sand\s/gi);
  243. for(var i in names) {
  244. var name = names[i];
  245. // skip empty names
  246. if (!name) continue;
  247. // Names in BibTeX can have three commas
  248. pieces = splitUnprotected(name, /\s*,\s*/g);
  249. var creator = {};
  250. if (pieces.length > 1) {
  251. creator.firstName = pieces.pop();
  252. creator.lastName = unescapeBibTeX(pieces.shift());
  253. if(pieces.length) {
  254. // If anything is left, it should only be the 'Jr' part
  255. creator.firstName += ', ' + pieces.join(', ');
  256. }
  257. creator.firstName = unescapeBibTeX(creator.firstName);
  258. creator.creatorType = field;
  259. } else if(splitUnprotected(name, / +/g).length > 1){
  260. creator = Zotero.Utilities.cleanAuthor(unescapeBibTeX(name), field, false);
  261. } else {
  262. creator = {
  263. lastName: unescapeBibTeX(name),
  264. creatorType: field,
  265. fieldMode: 1
  266. }
  267. }
  268. item.creators.push(creator);
  269. }
  270. } else if(field == "institution" || field == "organization") {
  271. item.backupPublisher = value;
  272. } else if(field == "number"){ // fix for techreport
  273. if (item.itemType == "report") {
  274. item.reportNumber = value;
  275. } else if (item.itemType == "book" || item.itemType == "bookSection") {
  276. item.seriesNumber = value;
  277. } else if (item.itemType == "patent"){
  278. item.patentNumber = value;
  279. } else {
  280. item.issue = value;
  281. }
  282. } else if(field == "month") {
  283. var monthIndex = months.indexOf(value.toLowerCase());
  284. if(monthIndex != -1) {
  285. value = Zotero.Utilities.formatDate({month:monthIndex});
  286. } else {
  287. value += " ";
  288. }
  289. if(item.date) {
  290. if(value.indexOf(item.date) != -1) {
  291. // value contains year and more
  292. item.date = value;
  293. } else {
  294. item.date = value+item.date;
  295. }
  296. } else {
  297. item.date = value;
  298. }
  299. } else if(field == "year") {
  300. if(item.date) {
  301. if(item.date.indexOf(value) == -1) {
  302. // date does not already contain year
  303. item.date += value;
  304. }
  305. } else {
  306. item.date = value;
  307. }
  308. } else if(field == "date") {
  309. //We're going to assume that "date" and the date parts don't occur together. If they do, we pick date, which should hold all.
  310. item.date = value;
  311. } else if(field == "pages") {
  312. if (item.itemType == "book" || item.itemType == "thesis" || item.itemType == "manuscript") {
  313. item.numPages = value;
  314. }
  315. else {
  316. item.pages = value.replace(/--/g, "-");
  317. }
  318. } else if(field == "note") {
  319. item._extraFields.push({raw: value.trim()});
  320. } else if(field == "howpublished") {
  321. if(value.length >= 7) {
  322. var str = value.substr(0, 7);
  323. if(str == "http://" || str == "https:/" || str == "mailto:") {
  324. item.url = value;
  325. } else {
  326. item._extraFields.push({field: 'Published', value: value});
  327. }
  328. }
  329. }
  330. //accept lastchecked or urldate for access date. These should never both occur.
  331. //If they do we don't know which is better so we might as well just take the second one
  332. else if (field == "lastchecked"|| field == "urldate"){
  333. item.accessDate = value;
  334. } else if(field == "keywords" || field == "keyword") {
  335. var re = new RegExp(keywordDelimRe, keywordDelimReFlags);
  336. if(!value.match(re) && keywordSplitOnSpace) {
  337. // keywords/tags
  338. item.tags = value.split(/\s+/);
  339. } else {
  340. item.tags = value.split(re);
  341. }
  342. } else if (field == "comment" || field == "annote" || field == "review" || field == "notes") {
  343. item.notes.push({note:Zotero.Utilities.text2html(value)});
  344. } else if (field == "pdf" || field == "path" /*Papers2 compatibility*/) {
  345. item.attachments.push({path:value, mimeType:"application/pdf"});
  346. } else if (field == "sentelink") { // the reference manager 'Sente' has a unique file scheme in exported BibTeX; it can occur multiple times
  347. item.attachments.push({path:value.split(",")[0], mimeType:"application/pdf"});
  348. } else if (field == "file") {
  349. var start = 0, attachment;
  350. rawValue = rawValue.replace(/\$\\backslash\$/g, '\\') // Mendeley invention?
  351. .replace(/([^\\](?:\\\\)*)\\(.){}/g, '$1$2'); // part of Mendeley's escaping (e.g. \~{} = ~)
  352. for(var i=0; i<rawValue.length; i++) {
  353. if(rawValue[i] == '\\') {
  354. i++; //skip next char
  355. continue;
  356. }
  357. if(rawValue[i] == ';') {
  358. attachment = parseFilePathRecord(rawValue.slice(start, i));
  359. if(attachment) item.attachments.push(attachment);
  360. start = i+1;
  361. }
  362. }
  363. attachment = parseFilePathRecord(rawValue.slice(start));
  364. if(attachment) item.attachments.push(attachment);
  365. } else if (field == "eprint" || field == "eprinttype") {
  366. // Support for IDs exported by BibLaTeX
  367. if(field == 'eprint') item._eprint = value;
  368. else item._eprinttype = value;
  369. var eprint = item._eprint;
  370. var eprinttype = item._eprinttype;
  371. // If we don't have both yet, continue
  372. if(!eprint || !eprinttype) return;
  373. var label = eprintIds[eprinttype.trim().toLowerCase()];
  374. if(!label) return;
  375. item._extraFields.push({field: label, value: eprint.trim()});
  376. delete item._eprinttype;
  377. delete item._eprint;
  378. } else if (extraIdentifiers[field]) {
  379. var label = extraIdentifiers[field];
  380. item._extraFields.push({field: label, value: value.trim()});
  381. }
  382. }
  383. /**
  384. * Split a string on a provided delimiter, but not if delimiter appears inside { }
  385. * @param {String} str String to split
  386. * @param {RegExp} delim RegExp object for the split delimiter. Use g flag to split on each
  387. * @return {String[]} Array of strings without delimiters
  388. */
  389. function splitUnprotected(str, delim) {
  390. delim.lastIndex = 0; // In case we're reusing a regexp
  391. var nextPossibleSplit = delim.exec(str);
  392. if(!nextPossibleSplit) return [str];
  393. var parts = [], open = 0, nextPartStart = 0;
  394. for(var i=0; i<str.length; i++) {
  395. if(i>nextPossibleSplit.index) {
  396. // Must have been inside braces
  397. nextPossibleSplit = delim.exec(str);
  398. if(!nextPossibleSplit) {
  399. parts.push(str.substr(nextPartStart));
  400. return parts;
  401. }
  402. }
  403. if(str[i] == '\\') {
  404. // Skip next character
  405. i++;
  406. continue;
  407. }
  408. if(str[i] == '{') {
  409. open++;
  410. continue;
  411. }
  412. if(str[i] == '}') {
  413. open--;
  414. if(open < 0) open = 0; // Shouldn't happen, but...
  415. continue;
  416. }
  417. if(open) continue;
  418. if(i == nextPossibleSplit.index) {
  419. parts.push(str.substring(nextPartStart, i));
  420. i += nextPossibleSplit[0].length - 1; // We can jump past the split delim
  421. nextPartStart = i + 1;
  422. nextPossibleSplit = delim.exec(str);
  423. if(!nextPossibleSplit) {
  424. parts.push(str.substr(nextPartStart));
  425. return parts;
  426. }
  427. }
  428. }
  429. // I don't think we should ever get here*, but just to be safe
  430. // *we should always be returning from the for loop
  431. var last = str.substr(nextPartStart).trim();
  432. if(last) parts.push(last);
  433. return parts;
  434. }
  435. function parseFilePathRecord(record) {
  436. var start = 0, fields = [];
  437. for(var i=0; i<record.length; i++) {
  438. if(record[i] == '\\') {
  439. i++;
  440. continue;
  441. }
  442. if(record[i] == ':') {
  443. fields.push(decodeFilePathComponent(record.slice(start, i)));
  444. start = i+1;
  445. }
  446. }
  447. fields.push(decodeFilePathComponent(record.slice(start)));
  448. if(fields.length != 3 && fields.length != 1) {
  449. Zotero.debug("Unknown file path record format: " + record);
  450. return;
  451. }
  452. var attachment = {};
  453. if(fields.length == 3) {
  454. attachment.title = fields[0].trim() || 'Attachment';
  455. attachment.path = fields[1];
  456. attachment.mimeType = fields[2];
  457. if(attachment.mimeType.search(/pdf/i) != -1) {
  458. attachment.mimeType = 'application/pdf';
  459. }
  460. } else {
  461. attachment.title = 'Attachment';
  462. attachment.path = fields[0];
  463. }
  464. attachment.path = attachment.path.trim();
  465. if(!attachment.path) return;
  466. return attachment;
  467. }
  468. function getFieldValue(read) {
  469. var value = "";
  470. // now, we have the first character of the field
  471. if(read == "{") {
  472. // character is a brace
  473. var openBraces = 1, nextAsLiteral = false;
  474. while(read = Zotero.read(1)) {
  475. if(nextAsLiteral) { // Previous character was a backslash
  476. value += read;
  477. nextAsLiteral = false;
  478. continue;
  479. }
  480. if(read == "\\") {
  481. value += read;
  482. nextAsLiteral = true;
  483. continue;
  484. }
  485. if(read == "{") {
  486. openBraces++;
  487. value += "{";
  488. } else if(read == "}") {
  489. openBraces--;
  490. if(openBraces == 0) {
  491. break;
  492. } else {
  493. value += "}";
  494. }
  495. } else {
  496. value += read;
  497. }
  498. }
  499. } else if(read == '"') {
  500. var openBraces = 0;
  501. while(read = Zotero.read(1)) {
  502. if(read == "{" && value[value.length-1] != "\\") {
  503. openBraces++;
  504. value += "{";
  505. } else if(read == "}" && value[value.length-1] != "\\") {
  506. openBraces--;
  507. value += "}";
  508. } else if(read == '"' && openBraces == 0) {
  509. break;
  510. } else {
  511. value += read;
  512. }
  513. }
  514. }
  515. return value;
  516. }
  517. function unescapeBibTeX(value) {
  518. if(value.length < 2) return value;
  519. // replace accented characters (yucky slow)
  520. value = value.replace(/{?(\\[`"'^~=]){?\\?([A-Za-z])}/g, "{$1$2}");
  521. //for special characters rendered by \[a-z] we need a space
  522. value = value.replace(/{?(\\[a-z]){?\\?([A-Za-z])}/g, "{$1 $2}");
  523. //convert tex markup into permitted HTML
  524. value = mapTeXmarkup(value);
  525. for (var mapped in reversemappingTable) { // really really slow!
  526. var unicode = reversemappingTable[mapped];
  527. while(value.indexOf(mapped) !== -1) {
  528. Zotero.debug("Replace " + mapped + " in " + value + " with " + unicode);
  529. value = value.replace(mapped, unicode);
  530. }
  531. mapped = mapped.replace(/[{}]/g, "");
  532. while(value.indexOf(mapped) !== -1) {
  533. //Z.debug(value)
  534. Zotero.debug("Replace(2) " + mapped + " in " + value + " with " + unicode);
  535. value = value.replace(mapped, unicode);
  536. }
  537. }
  538. // kill braces
  539. value = value.replace(/([^\\])[{}]+/g, "$1");
  540. if(value[0] == "{") {
  541. value = value.substr(1);
  542. }
  543. // chop off backslashes
  544. value = value.replace(/([^\\])\\([#$%&~_^\\{}])/g, "$1$2");
  545. value = value.replace(/([^\\])\\([#$%&~_^\\{}])/g, "$1$2");
  546. if(value[0] == "\\" && "#$%&~_^\\{}".indexOf(value[1]) != -1) {
  547. value = value.substr(1);
  548. }
  549. if(value[value.length-1] == "\\" && "#$%&~_^\\{}".indexOf(value[value.length-2]) != -1) {
  550. value = value.substr(0, value.length-1);
  551. }
  552. value = value.replace(/\\\\/g, "\\");
  553. value = value.replace(/\s+/g, " ");
  554. return value;
  555. }
  556. function jabrefSplit(str, sep) {
  557. var quoted = false;
  558. var result = [];
  559. str = str.split('');
  560. while (str.length > 0) {
  561. if (result.length == 0) { result = ['']; }
  562. if (str[0] == sep) {
  563. str.shift();
  564. result.push('');
  565. } else {
  566. if (str[0] == '\\') { str.shift(); }
  567. result[result.length - 1] += str.shift();
  568. }
  569. }
  570. return result;
  571. }
  572. function jabrefCollect(arr, func) {
  573. if (arr == null) { return []; }
  574. var result = [];
  575. for (var i = 0; i < arr.length; i++) {
  576. if (func(arr[i])) {
  577. result.push(arr[i]);
  578. }
  579. }
  580. return result;
  581. }
  582. function processComment() {
  583. var comment = "";
  584. var read;
  585. var collectionPath = [];
  586. var parentCollection, collection;
  587. while(read = Zotero.read(1)) {
  588. if (read == "}") { break; } // JabRef ought to escape '}' but doesn't; embedded '}' chars will break the import just as it will on JabRef itself
  589. comment += read;
  590. }
  591. if (comment == 'jabref-meta: groupsversion:3;') {
  592. jabref.format = 3;
  593. return;
  594. }
  595. if (comment.indexOf('jabref-meta: groupstree:') == 0) {
  596. if (jabref.format != 3) {
  597. Zotero.debug("jabref: fatal: unsupported group format: " + jabref.format);
  598. return;
  599. }
  600. comment = comment.replace(/^jabref-meta: groupstree:/, '').replace(/[\r\n]/gm, '')
  601. var records = jabrefSplit(comment, ';');
  602. while (records.length > 0) {
  603. var record = records.shift();
  604. var keys = jabrefSplit(record, ';');
  605. if (keys.length < 2) { continue; }
  606. var record = {id: keys.shift()};
  607. record.data = record.id.match(/^([0-9]) ([^:]*):(.*)/);
  608. if (record.data == null) {
  609. Zotero.debug("jabref: fatal: unexpected non-match for group " + record.id);
  610. return;
  611. }
  612. record.level = parseInt(record.data[1]);
  613. record.type = record.data[2]
  614. record.name = record.data[3]
  615. record.intersection = keys.shift(); // 0 = independent, 1 = intersection, 2 = union
  616. if (isNaN(record.level)) {
  617. Zotero.debug("jabref: fatal: unexpected record level in " + record.id);
  618. return;
  619. }
  620. if (record.level == 0) { continue; }
  621. if (record.type != 'ExplicitGroup') {
  622. Zotero.debug("jabref: fatal: group type " + record.type + " is not supported");
  623. return;
  624. }
  625. collectionPath = collectionPath.slice(0, record.level - 1).concat([record.name]);
  626. Zotero.debug("jabref: locating level " + record.level + ": " + collectionPath.join('/'));
  627. if (jabref.root.hasOwnProperty(collectionPath[0])) {
  628. collection = jabref.root[collectionPath[0]];
  629. Zotero.debug("jabref: root " + collection.name + " found");
  630. } else {
  631. collection = new Zotero.Collection();
  632. collection.name = collectionPath[0];
  633. collection.type = 'collection';
  634. collection.children = [];
  635. jabref.root[collectionPath[0]] = collection;
  636. Zotero.debug("jabref: root " + collection.name + " created");
  637. }
  638. parentCollection = null;
  639. for (var i = 1; i < collectionPath.length; i++) {
  640. var path = collectionPath[i];
  641. Zotero.debug("jabref: looking for child " + path + " under " + collection.name);
  642. var child = jabrefCollect(collection.children, function(n) { return (n.name == path)})
  643. if (child.length != 0) {
  644. child = child[0]
  645. Zotero.debug("jabref: child " + child.name + " found under " + collection.name);
  646. } else {
  647. child = new Zotero.Collection();
  648. child.name = path;
  649. child.type = 'collection';
  650. child.children = [];
  651. collection.children.push(child);
  652. Zotero.debug("jabref: child " + child.name + " created under " + collection.name);
  653. }
  654. parentCollection = collection;
  655. collection = child;
  656. }
  657. if (parentCollection) {
  658. parentCollection = jabrefCollect(parentCollection.children, function(n) { return (n.type == 'item') });
  659. }
  660. if (record.intersection == '2' && parentCollection) { // union with parent
  661. collection.children = parentCollection;
  662. }
  663. while(keys.length > 0) {
  664. key = keys.shift();
  665. if (key != '') {
  666. Zotero.debug('jabref: adding ' + key + ' to ' + collection.name);
  667. collection.children.push({type: 'item', id: key});
  668. }
  669. }
  670. if (parentCollection && record.intersection == '1') { // intersection with parent
  671. collection.children = jabrefMap(collection.children, function(n) { parentCollection.indexOf(n) !== -1; });
  672. }
  673. }
  674. }
  675. }
  676. function beginRecord(type, closeChar) {
  677. type = Zotero.Utilities.trimInternal(type.toLowerCase());
  678. if(type != "string") {
  679. var zoteroType = bibtex2zoteroTypeMap[type];
  680. if (!zoteroType) {
  681. Zotero.debug("discarded item from BibTeX; type was "+type);
  682. return;
  683. }
  684. var item = new Zotero.Item(zoteroType);
  685. item._extraFields = [];
  686. }
  687. var field = "";
  688. // by setting dontRead to true, we can skip a read on the next iteration
  689. // of this loop. this is useful after we read past the end of a string.
  690. var dontRead = false;
  691. var value, rawValue;
  692. while(dontRead || (read = Zotero.read(1))) {
  693. dontRead = false;
  694. if(read == "=") { // equals begin a field
  695. // read whitespace
  696. var read = Zotero.read(1);
  697. while(" \n\r\t".indexOf(read) != -1) {
  698. read = Zotero.read(1);
  699. }
  700. if(keyRe.test(read)) {
  701. // read numeric data here, since we might get an end bracket
  702. // that we should care about
  703. value = "";
  704. value += read;
  705. // character is a number
  706. while((read = Zotero.read(1)) && keyRe.test(read)) {
  707. value += read;
  708. }
  709. // don't read the next char; instead, process the character
  710. // we already read past the end of the string
  711. dontRead = true;
  712. // see if there's a defined string
  713. if(strings[value]) value = strings[value];
  714. } else {
  715. rawValue = getFieldValue(read);
  716. value = unescapeBibTeX(rawValue);
  717. }
  718. if(item) {
  719. processField(item, field.toLowerCase(), value, rawValue);
  720. } else if(type == "string") {
  721. strings[field] = value;
  722. }
  723. field = "";
  724. } else if(read == ",") { // commas reset
  725. if (item.itemID == null) {
  726. item.itemID = field; // itemID = citekey
  727. }
  728. field = "";
  729. } else if(read == closeChar) {
  730. if(item) {
  731. item.extra = extraFieldsToString(item._extraFields);
  732. delete item._extraFields;
  733. if (!item.publisher && item.backupPublisher){
  734. item.publisher=item.backupPublisher;
  735. delete item.backupPublisher;
  736. }
  737. item.complete();
  738. }
  739. return;
  740. } else if(" \n\r\t".indexOf(read) == -1) { // skip whitespace
  741. field += read;
  742. }
  743. }
  744. }
  745. function doImport() {
  746. var read = "", text = "", recordCloseElement = false;
  747. var type = false;
  748. while(read = Zotero.read(1)) {
  749. if(read == "@") {
  750. type = "";
  751. } else if(type !== false) {
  752. if(type == "comment") {
  753. processComment();
  754. type = false;
  755. } else if(read == "{") { // possible open character
  756. beginRecord(type, "}");
  757. type = false;
  758. } else if(read == "(") { // possible open character
  759. beginRecord(type, ")");
  760. type = false;
  761. } else if(/[a-zA-Z0-9-_]/.test(read)) {
  762. type += read;
  763. }
  764. }
  765. }
  766. for (var key in jabref.root) {
  767. if (jabref.root.hasOwnProperty(key)) { jabref.root[key].complete(); }
  768. }
  769. }
  770. // some fields are, in fact, macros. If that is the case then we should not put the
  771. // data in the braces as it will cause the macros to not expand properly
  772. function writeField(field, value, isMacro) {
  773. if (!value && typeof value != "number") return;
  774. value = value + ""; // convert integers to strings
  775. Zotero.write(",\n\t" + field + " = ");
  776. if (!isMacro) Zotero.write("{");
  777. // url field is preserved, for use with \href and \url
  778. // Other fields (DOI?) may need similar treatment
  779. if (!isMacro && !(field == "url" || field == "doi" || field == "file" || field == "lccn" )) {
  780. // I hope these are all the escape characters!
  781. value = value.replace(/[|\<\>\~\^\\\{\}]/g, mapEscape).replace(/([\#\$\%\&\_])/g, "\\$1");
  782. //disable
  783. /** if (field == "title" || field == "type" || field == "shorttitle" || field == "booktitle" || field == "series") {
  784. if (!isTitleCase(value)) {
  785. //protect caps for everything but the first letter
  786. value = value.replace(/(.)([A-Z]+)/g, "$1{$2}");
  787. } else { //protect all-caps vords and initials
  788. value = value.replace(/([\s.-])([A-Z]+)(?=\.)/g, "$1{$2}"); //protect initials
  789. if(value.toUpperCase() != value) value = value.replace(/(\s)([A-Z]{2,})(?=[\.,\s]|$)/g, "$1{$2}");
  790. }
  791. }
  792. **/
  793. // Case of words with uppercase characters in non-initial positions is preserved with braces.
  794. // we're looking at all unicode letters
  795. var protectCaps = new ZU.XRegExp("\\b\\p{Letter}+\\p{Uppercase_Letter}\\p{Letter}*", 'g')
  796. if (field != "pages") {
  797. value = ZU.XRegExp.replace(value, protectCaps, "{$0}");
  798. }
  799. }
  800. if (Zotero.getOption("exportCharset") != "UTF-8") {
  801. value = value.replace(/[\u0080-\uFFFF]/g, mapAccent);
  802. }
  803. //convert the HTML markup allowed in Zotero for rich text to TeX; excluding doi/url/file shouldn't be necessary, but better to be safe;
  804. if (!((field == "url") || (field == "doi") || (field == "file"))) value = mapHTMLmarkup(value);
  805. Zotero.write(value);
  806. if (!isMacro) Zotero.write("}");
  807. }
  808. function mapHTMLmarkup(characters){
  809. //converts the HTML markup allowed in Zotero for rich text to TeX
  810. //since < and > have already been escaped, we need this rather hideous code - I couldn't see a way around it though.
  811. //italics and bold
  812. characters = characters.replace(/\{\\textless\}i\{\\textgreater\}(.+?)\{\\textless\}\/i{\\textgreater\}/g, "\\textit{$1}")
  813. .replace(/\{\\textless\}b\{\\textgreater\}(.+?)\{\\textless\}\/b{\\textgreater\}/g, "\\textbf{$1}");
  814. //sub and superscript
  815. characters = characters.replace(/\{\\textless\}sup\{\\textgreater\}(.+?)\{\\textless\}\/sup{\\textgreater\}/g, "\$^{\\textrm{$1}}\$")
  816. .replace(/\{\\textless\}sub\{\\textgreater\}(.+?)\{\\textless\}\/sub\{\\textgreater\}/g, "\$_{\\textrm{$1}}\$");
  817. //two variants of small caps
  818. characters = characters.replace(/\{\\textless\}span\sstyle=\"small\-caps\"\{\\textgreater\}(.+?)\{\\textless\}\/span{\\textgreater\}/g, "\\textsc{$1}")
  819. .replace(/\{\\textless\}sc\{\\textgreater\}(.+?)\{\\textless\}\/sc\{\\textgreater\}/g, "\\textsc{$1}");
  820. return characters;
  821. }
  822. function mapTeXmarkup(tex){
  823. //reverse of the above - converts tex mark-up into html mark-up permitted by Zotero
  824. //italics and bold
  825. tex = tex.replace(/\\textit\{([^\}]+\})/g, "<i>$1</i>").replace(/\\textbf\{([^\}]+\})/g, "<b>$1</b>");
  826. //two versions of subscript the .* after $ is necessary because people m
  827. tex = tex.replace(/\$[^\{\$]*_\{([^\}]+\})\$/g, "<sub>$1</sub>").replace(/\$[^\{]*_\{\\textrm\{([^\}]+\}\})/g, "<sub>$1</sub>");
  828. //two version of superscript
  829. tex = tex.replace(/\$[^\{]*\^\{([^\}]+\}\$)/g, "<sup>$1</sup>").replace(/\$[^\{]*\^\{\\textrm\{([^\}]+\}\})/g, "<sup>$1</sup>");
  830. //small caps
  831. tex = tex.replace(/\\textsc\{([^\}]+)/g, "<span style=\"small-caps\">$1</span>");
  832. return tex;
  833. }
  834. //Disable the isTitleCase function until we decide what to do with it.
  835. /* const skipWords = ["but", "or", "yet", "so", "for", "and", "nor",
  836. "a", "an", "the", "at", "by", "from", "in", "into", "of", "on",
  837. "to", "with", "up", "down", "as", "while", "aboard", "about",
  838. "above", "across", "after", "against", "along", "amid", "among",
  839. "anti", "around", "as", "before", "behind", "below", "beneath",
  840. "beside", "besides", "between", "beyond", "but", "despite",
  841. "down", "during", "except", "for", "inside", "like", "near",
  842. "off", "onto", "over", "past", "per", "plus", "round", "save",
  843. "since", "than", "through", "toward", "towards", "under",
  844. "underneath", "unlike", "until", "upon", "versus", "via",
  845. "within", "without"];
  846. function isTitleCase(string) {
  847. const wordRE = /[\s[(]([^\s,\.:?!\])]+)/g;
  848. var word;
  849. while (word = wordRE.exec(string)) {
  850. word = word[1];
  851. if(word.search(/\d/) != -1 //ignore words with numbers (including just numbers)
  852. || skipWords.indexOf(word.toLowerCase()) != -1) {
  853. continue;
  854. }
  855. if(word.toLowerCase() == word) return false;
  856. }
  857. return true;
  858. }
  859. */
  860. function mapEscape(character) {
  861. return alwaysMap[character];
  862. }
  863. function mapAccent(character) {
  864. return (mappingTable[character] ? mappingTable[character] : "?");
  865. }
  866. var filePathSpecialChars = '\\\\:;{}$'; // $ for Mendeley
  867. var encodeFilePathRE = new RegExp('[' + filePathSpecialChars + ']', 'g');
  868. function encodeFilePathComponent(value) {
  869. return value.replace(encodeFilePathRE, "\\$&");
  870. }
  871. function decodeFilePathComponent(value) {
  872. return value.replace(/\\([^A-Za-z0-9.])/g, "$1");
  873. }
  874. // a little substitution function for BibTeX keys, where we don't want LaTeX
  875. // escaping, but we do want to preserve the base characters
  876. function tidyAccents(s) {
  877. var r=s.toLowerCase();
  878. // XXX Remove conditional when we drop Zotero 2.1.x support
  879. // This is supported in Zotero 3.0 and higher
  880. if (ZU.removeDiacritics !== undefined)
  881. r = ZU.removeDiacritics(r, true);
  882. else {
  883. // We fall back on the replacement list we used previously
  884. r = r.replace(new RegExp("[ä]", 'g'),"ae");
  885. r = r.replace(new RegExp("[ö]", 'g'),"oe");
  886. r = r.replace(new RegExp("[Ă¼]", 'g'),"ue");
  887. r = r.replace(new RegExp("[Ă Ă¡Ă¢Ă£Ă¥]", 'g'),"a");
  888. r = r.replace(new RegExp("æ", 'g'),"ae");
  889. r = r.replace(new RegExp("ç", 'g'),"c");
  890. r = r.replace(new RegExp("[Ă¨Ă©ĂªĂ«]", 'g'),"e");
  891. r = r.replace(new RegExp("[Ă¬Ă­Ă®Ă¯]", 'g'),"i");
  892. r = r.replace(new RegExp("ñ", 'g'),"n");
  893. r = r.replace(new RegExp("[Ă²Ă³Ă´Ăµ]", 'g'),"o");
  894. r = r.replace(new RegExp("Å“", 'g'),"oe");
  895. r = r.replace(new RegExp("[Ă¹ĂºĂ»]", 'g'),"u");
  896. r = r.replace(new RegExp("[Ă½Ă¿]", 'g'),"y");
  897. }
  898. return r;
  899. };
  900. var numberRe = /^[0-9]+/;
  901. // Below is a list of words that should not appear as part of the citation key
  902. // in includes the indefinite articles of English, German, French and Spanish, as well as a small set of English prepositions whose
  903. // force is more grammatical than lexical, i.e. which are likely to strike many as 'insignificant'.
  904. // The assumption is that most who want a title word in their key would prefer the first word of significance.
  905. var citeKeyTitleBannedRe = /\b(a|an|the|some|from|on|in|to|of|do|with|der|die|das|ein|eine|einer|eines|einem|einen|un|une|la|le|l\'|el|las|los|al|uno|una|unos|unas|de|des|del|d\')(\s+|\b)/g;
  906. var citeKeyConversionsRe = /%([a-zA-Z])/;
  907. var citeKeyCleanRe = /[^a-z0-9\!\$\&\*\+\-\.\/\:\;\<\>\?\[\]\^\_\`\|]+/g;
  908. var citeKeyConversions = {
  909. "a":function (flags, item) {
  910. if(item.creators && item.creators[0] && item.creators[0].lastName) {
  911. return item.creators[0].lastName.toLowerCase().replace(/ /g,"_").replace(/,/g,"");
  912. }
  913. return "";
  914. },
  915. "t":function (flags, item) {
  916. if (item["title"]) {
  917. return item["title"].toLowerCase().replace(citeKeyTitleBannedRe, "").split(/\s+/g)[0];
  918. }
  919. return "";
  920. },
  921. "y":function (flags, item) {
  922. if(item.date) {
  923. var date = Zotero.Utilities.strToDate(item.date);
  924. if(date.year && numberRe.test(date.year)) {
  925. return date.year;
  926. }
  927. }
  928. return "????";
  929. }
  930. }
  931. function buildCiteKey (item,citekeys) {
  932. var basekey = "";
  933. var counter = 0;
  934. citeKeyFormatRemaining = citeKeyFormat;
  935. while (citeKeyConversionsRe.test(citeKeyFormatRemaining)) {
  936. if (counter > 100) {
  937. Zotero.debug("Pathological BibTeX format: " + citeKeyFormat);
  938. break;
  939. }
  940. var m = citeKeyFormatRemaining.match(citeKeyConversionsRe);
  941. if (m.index > 0) {
  942. //add data before the conversion match to basekey
  943. basekey = basekey + citeKeyFormatRemaining.substr(0, m.index);
  944. }
  945. var flags = ""; // for now
  946. var f = citeKeyConversions[m[1]];
  947. if (typeof(f) == "function") {
  948. var value = f(flags, item);
  949. Zotero.debug("Got value " + value + " for %" + m[1]);
  950. //add conversion to basekey
  951. basekey = basekey + value;
  952. }
  953. citeKeyFormatRemaining = citeKeyFormatRemaining.substr(m.index + m.length);
  954. counter++;
  955. }
  956. if (citeKeyFormatRemaining.length > 0) {
  957. basekey = basekey + citeKeyFormatRemaining;
  958. }
  959. // for now, remove any characters not explicitly known to be allowed;
  960. // we might want to allow UTF-8 citation keys in the future, depending
  961. // on implementation support.
  962. //
  963. // no matter what, we want to make sure we exclude
  964. // " # % ' ( ) , = { } ~ and backslash
  965. // however, we want to keep the base characters
  966. basekey = tidyAccents(basekey);
  967. basekey = basekey.replace(citeKeyCleanRe, "");
  968. var citekey = basekey;
  969. var i = 0;
  970. while(citekeys[citekey]) {
  971. i++;
  972. citekey = basekey + "-" + i;
  973. }
  974. citekeys[citekey] = true;
  975. return citekey;
  976. }
  977. function doExport() {
  978. //Zotero.write("% BibTeX export generated by Zotero "+Zotero.Utilities.getVersion());
  979. // to make sure the BOM gets ignored
  980. Zotero.write("\n");
  981. var first = true;
  982. var citekeys = new Object();
  983. var item;
  984. while(item = Zotero.nextItem()) {
  985. //don't export standalone notes and attachments
  986. if(item.itemType == "note" || item.itemType == "attachment") continue;
  987. // determine type
  988. var type = zotero2bibtexTypeMap[item.itemType];
  989. if (typeof(type) == "function") { type = type(item); }
  990. if(!type) type = "misc";
  991. // create a unique citation key
  992. var citekey = buildCiteKey(item, citekeys);
  993. // write citation key
  994. Zotero.write((first ? "" : "\n\n") + "@"+type+"{"+citekey);
  995. first = false;
  996. for(var field in fieldMap) {
  997. if(item[fieldMap[field]]) {
  998. writeField(field, item[fieldMap[field]]);
  999. }
  1000. }
  1001. if(item.reportNumber || item.issue || item.seriesNumber || item.patentNumber) {
  1002. writeField("number", item.reportNumber || item.issue || item.seriesNumber|| item.patentNumber);
  1003. }
  1004. if (item.accessDate){
  1005. var accessYMD = item.accessDate.replace(/\s*\d+:\d+:\d+/, "");
  1006. writeField("urldate", accessYMD);
  1007. }
  1008. if(item.publicationTitle) {
  1009. if(item.itemType == "bookSection" || item.itemType == "conferencePaper") {
  1010. writeField("booktitle", item.publicationTitle);
  1011. } else if(Zotero.getOption("useJournalAbbreviation") && item.journalAbbreviation){
  1012. writeField("journal", item.journalAbbreviation);
  1013. } else {
  1014. writeField("journal", item.publicationTitle);
  1015. }
  1016. }
  1017. if(item.publisher) {
  1018. if(item.itemType == "thesis") {
  1019. writeField("school", item.publisher);
  1020. } else if(item.itemType =="report") {
  1021. writeField("institution", item.publisher);
  1022. } else {
  1023. writeField("publisher", item.publisher);
  1024. }
  1025. }
  1026. if(item.creators && item.creators.length) {
  1027. // split creators into subcategories
  1028. var author = "";
  1029. var editor = "";
  1030. var translator = "";
  1031. var collaborator = "";
  1032. var primaryCreatorType = Zotero.Utilities.getCreatorsForType(item.itemType)[0];
  1033. for(var i in item.creators) {
  1034. var creator = item.creators[i];
  1035. var creatorString;
  1036. if (creator.firstName) {
  1037. var fname = creator.firstName.split(/\s*,!?\s*/);
  1038. fname.push(fname.shift()); // If we have a Jr. part(s), it should precede first name
  1039. creatorString = creator.lastName + ", " + fname.join(', ');
  1040. } else {
  1041. creatorString = creator.lastName;
  1042. }
  1043. creatorString = creatorString.replace(/[|\<\>\~\^\\\{\}]/g, mapEscape)
  1044. .replace(/([\#\$\%\&\_])/g, "\\$1");
  1045. if (creator.fieldMode == true) { // fieldMode true, assume corporate author
  1046. creatorString = "{" + creatorString + "}";
  1047. } else {
  1048. creatorString = creatorString.replace(/ (and) /gi, ' {$1} ');
  1049. }
  1050. if (creator.creatorType == "editor" || creator.creatorType == "seriesEditor") {
  1051. editor += " and "+creatorString;
  1052. } else if (creator.creatorType == "translator") {
  1053. translator += " and "+creatorString;
  1054. } else if (creator.creatorType == primaryCreatorType) {
  1055. author += " and "+creatorString;
  1056. } else {
  1057. collaborator += " and "+creatorString;
  1058. }
  1059. }
  1060. if(author) {
  1061. writeField("author", "{" + author.substr(5) + "}", true);
  1062. }
  1063. if(editor) {
  1064. writeField("editor", "{" + editor.substr(5) + "}", true);
  1065. }
  1066. if(translator) {
  1067. writeField("translator", "{" + translator.substr(5) + "}", true);
  1068. }
  1069. if(collaborator) {
  1070. writeField("collaborator", "{" + collaborator.substr(5) + "}", true);
  1071. }
  1072. }
  1073. if(item.date) {
  1074. var date = Zotero.Utilities.strToDate(item.date);
  1075. // need to use non-localized abbreviation
  1076. if(typeof date.month == "number") {
  1077. writeField("month", months[date.month], true);
  1078. }
  1079. if(date.year) {
  1080. writeField("year", date.year);
  1081. }
  1082. }
  1083. if(item.extra) {
  1084. // Export identifiers
  1085. var extraFields = parseExtraFields(item.extra);
  1086. for(var i=0; i<extraFields.length; i++) {
  1087. var rec = extraFields[i];
  1088. if(!rec.field || !revExtraIds[rec.field]) continue;
  1089. var value = rec.value.trim();
  1090. if(value) {
  1091. writeField(revExtraIds[rec.field], '{'+value+'}', true);
  1092. extraFields.splice(i, 1);
  1093. i--;
  1094. }
  1095. }
  1096. var extra = extraFieldsToString(extraFields); // Make sure we join exactly with what we split
  1097. if(extra) writeField("note", extra);
  1098. }
  1099. if(item.tags && item.tags.length) {
  1100. var tagString = "";
  1101. for(var i in item.tags) {
  1102. var tag = item.tags[i];
  1103. tagString += ", "+tag.tag;
  1104. }
  1105. writeField("keywords", tagString.substr(2));
  1106. }
  1107. if(item.pages) {
  1108. writeField("pages", item.pages.replace("-","--"));
  1109. }
  1110. // Commented out, because we don't want a books number of pages in the BibTeX "pages" field for books.
  1111. //if(item.numPages) {
  1112. // writeField("pages", item.numPages);
  1113. //}
  1114. /* We'll prefer url over howpublished see
  1115. https://forums.zotero.org/discussion/24554/bibtex-doubled-url/#Comment_157802
  1116. if(item.itemType == "webpage") {
  1117. writeField("howpublished", item.url);
  1118. }*/
  1119. if (item.notes && Zotero.getOption("exportNotes")) {
  1120. for(var i in item.notes) {
  1121. var note = item.notes[i];
  1122. writeField("annote", Zotero.Utilities.unescapeHTML(note["note"]));
  1123. }
  1124. }
  1125. if(item.attachments) {
  1126. var attachmentString = "";
  1127. for(var i in item.attachments) {
  1128. var attachment = item.attachments[i];
  1129. if(Zotero.getOption("exportFileData") && attachment.saveFile) {
  1130. attachment.saveFile(attachment.defaultPath, true);
  1131. attachmentString += ";" + encodeFilePathComponent(attachment.title)
  1132. + ":" + encodeFilePathComponent(attachment.defaultPath)
  1133. + ":" + encodeFilePathComponent(attachment.mimeType);
  1134. } else if(attachment.localPath) {
  1135. attachmentString += ";" + encodeFilePathComponent(attachment.title)
  1136. + ":" + encodeFilePathComponent(attachment.localPath)
  1137. + ":" + encodeFilePathComponent(attachment.mimeType);
  1138. }
  1139. }
  1140. if(attachmentString) {
  1141. writeField("file", attachmentString.substr(1));
  1142. }
  1143. }
  1144. Zotero.write("\n}");
  1145. }
  1146. }
  1147. var exports = {
  1148. "doExport": doExport,
  1149. "doImport": doImport,
  1150. "setKeywordDelimRe": setKeywordDelimRe,
  1151. "setKeywordSplitOnSpace": setKeywordSplitOnSpace
  1152. }
  1153. /*
  1154. * new mapping table based on that from Matthias Steffens,
  1155. * then enhanced with some fields generated from the unicode table.
  1156. */
  1157. var mappingTable = {
  1158. "\u00A0":"~", // NO-BREAK SPACE
  1159. "\u00A1":"{\\textexclamdown}", // INVERTED EXCLAMATION MARK
  1160. "\u00A2":"{\\textcent}", // CENT SIGN
  1161. "\u00A3":"{\\textsterling}", // POUND SIGN
  1162. "\u00A5":"{\\textyen}", // YEN SIGN
  1163. "\u00A6":"{\\textbrokenbar}", // BROKEN BAR
  1164. "\u00A7":"{\\textsection}", // SECTION SIGN
  1165. "\u00A8":"{\\textasciidieresis}", // DIAERESIS
  1166. "\u00A9":"{\\textcopyright}", // COPYRIGHT SIGN
  1167. "\u00AA":"{\\textordfeminine}", // FEMININE ORDINAL INDICATOR
  1168. "\u00AB":"{\\guillemotleft}", // LEFT-POINTING DOUBLE ANGLE QUOTATION MARK
  1169. "\u00AC":"{\\textlnot}", // NOT SIGN
  1170. "\u00AD":"-", // SOFT HYPHEN
  1171. "\u00AE":"{\\textregistered}", // REGISTERED SIGN
  1172. "\u00AF":"{\\textasciimacron}", // MACRON
  1173. "\u00B0":"{\\textdegree}", // DEGREE SIGN
  1174. "\u00B1":"{\\textpm}", // PLUS-MINUS SIGN
  1175. "\u00B2":"{\\texttwosuperior}", // SUPERSCRIPT TWO
  1176. "\u00B3":"{\\textthreesuperior}", // SUPERSCRIPT THREE
  1177. "\u00B4":"{\\textasciiacute}", // ACUTE ACCENT
  1178. "\u00B5":"{\\textmu}", // MICRO SIGN
  1179. "\u00B6":"{\\textparagraph}", // PILCROW SIGN
  1180. "\u00B7":"{\\textperiodcentered}", // MIDDLE DOT
  1181. "\u00B8":"{\\c\\ }", // CEDILLA
  1182. "\u00B9":"{\\textonesuperior}", // SUPERSCRIPT ONE
  1183. "\u00BA":"{\\textordmasculine}", // MASCULINE ORDINAL INDICATOR
  1184. "\u00BB":"{\\guillemotright}", // RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK
  1185. "\u00BC":"{\\textonequarter}", // VULGAR FRACTION ONE QUARTER
  1186. "\u00BD":"{\\textonehalf}", // VULGAR FRACTION ONE HALF
  1187. "\u00BE":"{\\textthreequarters}", // VULGAR FRACTION THREE QUARTERS
  1188. "\u00BF":"{\\textquestiondown}", // INVERTED QUESTION MARK
  1189. "\u00C6":"{\\AE}", // LATIN CAPITAL LETTER AE
  1190. "\u00D0":"{\\DH}", // LATIN CAPITAL LETTER ETH
  1191. "\u00D7":"{\\texttimes}", // MULTIPLICATION SIGN
  1192. "\u00D8":"{\\O}", // LATIN CAPITAL LETTER O WITH STROKE
  1193. "\u00DE":"{\\TH}", // LATIN CAPITAL LETTER THORN
  1194. "\u00DF":"{\\ss}", // LATIN SMALL LETTER SHARP S
  1195. "\u00E6":"{\\ae}", // LATIN SMALL LETTER AE
  1196. "\u00F0":"{\\dh}", // LATIN SMALL LETTER ETH
  1197. "\u00F7":"{\\textdiv}", // DIVISION SIGN
  1198. "\u00F8":"{\\o}", // LATIN SMALL LETTER O WITH STROKE
  1199. "\u00FE":"{\\th}", // LATIN SMALL LETTER THORN
  1200. "\u0131":"{\\i}", // LATIN SMALL LETTER DOTLESS I
  1201. "\u0132":"IJ", // LATIN CAPITAL LIGATURE IJ
  1202. "\u0133":"ij", // LATIN SMALL LIGATURE IJ
  1203. "\u0138":"k", // LATIN SMALL LETTER KRA
  1204. "\u0149":"'n", // LATIN SMALL LETTER N PRECEDED BY APOSTROPHE
  1205. "\u014A":"{\\NG}", // LATIN CAPITAL LETTER ENG
  1206. "\u014B":"{\\ng}", // LATIN SMALL LETTER ENG
  1207. "\u0152":"{\\OE}", // LATIN CAPITAL LIGATURE OE
  1208. "\u0153":"{\\oe}", // LATIN SMALL LIGATURE OE
  1209. "\u017F":"s", // LATIN SMALL LETTER LONG S
  1210. "\u02B9":"'", // MODIFIER LETTER PRIME
  1211. "\u02BB":"'", // MODIFIER LETTER TURNED COMMA
  1212. "\u02BC":"'", // MODIFIER LETTER APOSTROPHE
  1213. "\u02BD":"'", // MODIFIER LETTER REVERSED COMMA
  1214. "\u02C6":"{\\textasciicircum}", // MODIFIER LETTER CIRCUMFLEX ACCENT
  1215. "\u02C8":"'", // MODIFIER LETTER VERTICAL LINE
  1216. "\u02C9":"-", // MODIFIER LETTER MACRON
  1217. "\u02CC":",", // MODIFIER LETTER LOW VERTICAL LINE
  1218. "\u02D0":":", // MODIFIER LETTER TRIANGULAR COLON
  1219. "\u02DA":"o", // RING ABOVE
  1220. "\u02DC":"\\~{}", // SMALL TILDE
  1221. "\u02DD":"{\\textacutedbl}", // DOUBLE ACUTE ACCENT
  1222. "\u0374":"'", // GREEK NUMERAL SIGN
  1223. "\u0375":",", // GREEK LOWER NUMERAL SIGN
  1224. "\u037E":";", // GREEK QUESTION MARK
  1225. //Greek letters courtesy of spartanroc
  1226. "\u0393":"$\\Gamma$", // GREEK Gamma
  1227. "\u0394":"$\\Delta$", // GREEK Delta
  1228. "\u0398":"$\\Theta$", // GREEK Theta
  1229. "\u039B":"$\\Lambda$", // GREEK Lambda
  1230. "\u039E":"$\\Xi$", // GREEK Xi
  1231. "\u03A0":"$\\Pi$", // GREEK Pi
  1232. "\u03A3":"$\\Sigma$", // GREEK Sigma
  1233. "\u03A6":"$\\Phi$", // GREEK Phi
  1234. "\u03A8":"$\\Psi$", // GREEK Psi
  1235. "\u03A9":"$\\Omega$", // GREEK Omega
  1236. "\u03B1":"$\\alpha$", // GREEK alpha
  1237. "\u03B2":"$\\beta$", // GREEK beta
  1238. "\u03B3":"$\\gamma$", // GREEK gamma
  1239. "\u03B4":"$\\delta$", // GREEK delta
  1240. "\u03B5":"$\\varepsilon$", // GREEK var-epsilon
  1241. "\u03B6":"$\\zeta$", // GREEK zeta
  1242. "\u03B7":"$\\eta$", // GREEK eta
  1243. "\u03B8":"$\\theta$", // GREEK theta
  1244. "\u03B9":"$\\iota$", // GREEK iota
  1245. "\u03BA":"$\\kappa$", // GREEK kappa
  1246. "\u03BB":"$\\lambda$", // GREEK lambda
  1247. "\u03BC":"$\\mu$", // GREEK mu
  1248. "\u03BD":"$\\nu$", // GREEK nu
  1249. "\u03BE":"$\\xi$", // GREEK xi
  1250. "\u03C0":"$\\pi$", // GREEK pi
  1251. "\u03C1":"$\\rho$", // GREEK rho
  1252. "\u03C2":"$\\varsigma$", // GREEK var-sigma
  1253. "\u03C3":"$\\sigma$", // GREEK sigma
  1254. "\u03C4":"$\\tau$", // GREEK tau
  1255. "\u03C5":"$\\upsilon$", // GREEK upsilon
  1256. "\u03C6":"$\\varphi$", // GREEK var-phi
  1257. "\u03C7":"$\\chi$", // GREEK chi
  1258. "\u03C8":"$\\psi$", // GREEK psi
  1259. "\u03C9":"$\\omega$", // GREEK omega
  1260. "\u03D1":"$\\vartheta$", // GREEK var-theta
  1261. "\u03D2":"$\\Upsilon$", // GREEK Upsilon
  1262. "\u03D5":"$\\phi$", // GREEK phi
  1263. "\u03D6":"$\\varpi$", // GREEK var-pi
  1264. "\u03F1":"$\\varrho$", // GREEK var-rho
  1265. "\u03F5":"$\\epsilon$", // GREEK epsilon
  1266. //Greek letters end
  1267. "\u2000":" ", // EN QUAD
  1268. "\u2001":" ", // EM QUAD
  1269. "\u2002":" ", // EN SPACE
  1270. "\u2003":" ", // EM SPACE
  1271. "\u2004":" ", // THREE-PER-EM SPACE
  1272. "\u2005":" ", // FOUR-PER-EM SPACE
  1273. "\u2006":" ", // SIX-PER-EM SPACE
  1274. "\u2007":" ", // FIGURE SPACE
  1275. "\u2008":" ", // PUNCTUATION SPACE
  1276. "\u2009":" ", // THIN SPACE
  1277. "\u2010":"-", // HYPHEN
  1278. "\u2011":"-", // NON-BREAKING HYPHEN
  1279. "\u2012":"-", // FIGURE DASH
  1280. "\u2013":"{\\textendash}", // EN DASH
  1281. "\u2014":"{\\textemdash}", // EM DASH
  1282. "\u2015":"{\\textemdash}", // HORIZONTAL BAR or QUOTATION DASH (not in LaTeX -- use EM DASH)
  1283. "\u2016":"{\\textbardbl}", // DOUBLE VERTICAL LINE
  1284. "\u2017":"{\\textunderscore}", // DOUBLE LOW LINE
  1285. "\u2018":"{\\textquoteleft}", // LEFT SINGLE QUOTATION MARK
  1286. "\u2019":"{\\textquoteright}", // RIGHT SINGLE QUOTATION MARK
  1287. "`" : "\u2018", // LEFT SINGLE QUOTATION MARK
  1288. "'" : "\u2019", // RIGHT SINGLE QUOTATION MARK
  1289. "\u201A":"{\\quotesinglbase}", // SINGLE LOW-9 QUOTATION MARK
  1290. "\u201B":"'", // SINGLE HIGH-REVERSED-9 QUOTATION MARK
  1291. "\u201C":"{\\textquotedblleft}", // LEFT DOUBLE QUOTATION MARK
  1292. "\u201D":"{\\textquotedblright}", // RIGHT DOUBLE QUOTATION MARK
  1293. "\u201E":"{\\quotedblbase}", // DOUBLE LOW-9 QUOTATION MARK
  1294. "\u201F":"{\\quotedblbase}", // DOUBLE HIGH-REVERSED-9 QUOTATION MARK
  1295. "\u2020":"{\\textdagger}", // DAGGER
  1296. "\u2021":"{\\textdaggerdbl}", // DOUBLE DAGGER
  1297. "\u2022":"{\\textbullet}", // BULLET
  1298. "\u2023":">", // TRIANGULAR BULLET
  1299. "\u2024":".", // ONE DOT LEADER
  1300. "\u2025":"..", // TWO DOT LEADER
  1301. "\u2026":"{\\textellipsis}", // HORIZONTAL ELLIPSIS
  1302. "\u2027":"-", // HYPHENATION POINT
  1303. "\u202F":" ", // NARROW NO-BREAK SPACE
  1304. "\u2030":"{\\textperthousand}", // PER MILLE SIGN
  1305. "\u2032":"'", // PRIME
  1306. "\u2033":"'", // DOUBLE PRIME
  1307. "\u2034":"'''", // TRIPLE PRIME
  1308. "\u2035":"`", // REVERSED PRIME
  1309. "\u2036":"``", // REVERSED DOUBLE PRIME
  1310. "\u2037":"```", // REVERSED TRIPLE PRIME
  1311. "\u2039":"{\\guilsinglleft}", // SINGLE LEFT-POINTING ANGLE QUOTATION MARK
  1312. "\u203A":"{\\guilsinglright}", // SINGLE RIGHT-POINTING ANGLE QUOTATION MARK
  1313. "\u203C":"!!", // DOUBLE EXCLAMATION MARK
  1314. "\u203E":"-", // OVERLINE
  1315. "\u2043":"-", // HYPHEN BULLET
  1316. "\u2044":"{\\textfractionsolidus}", // FRACTION SLASH
  1317. "\u2048":"?!", // QUESTION EXCLAMATION MARK
  1318. "\u2049":"!?", // EXCLAMATION QUESTION MARK
  1319. "\u204A":"7", // TIRONIAN SIGN ET
  1320. "\u2070":"$^{0}$", // SUPERSCRIPT ZERO
  1321. "\u2074":"$^{4}$", // SUPERSCRIPT FOUR
  1322. "\u2075":"$^{5}$", // SUPERSCRIPT FIVE
  1323. "\u2076":"$^{6}$", // SUPERSCRIPT SIX
  1324. "\u2077":"$^{7}$", // SUPERSCRIPT SEVEN
  1325. "\u2078":"$^{8}$", // SUPERSCRIPT EIGHT
  1326. "\u2079":"$^{9}$", // SUPERSCRIPT NINE
  1327. "\u207A":"$^{+}$", // SUPERSCRIPT PLUS SIGN
  1328. "\u207B":"$^{-}$", // SUPERSCRIPT MINUS
  1329. "\u207C":"$^{=}$", // SUPERSCRIPT EQUALS SIGN
  1330. "\u207D":"$^{(}$", // SUPERSCRIPT LEFT PARENTHESIS
  1331. "\u207E":"$^{)}$", // SUPERSCRIPT RIGHT PARENTHESIS
  1332. "\u207F":"$^{n}$", // SUPERSCRIPT LATIN SMALL LETTER N
  1333. "\u2080":"$_{0}$", // SUBSCRIPT ZERO
  1334. "\u2081":"$_{1}$", // SUBSCRIPT ONE
  1335. "\u2082":"$_{2}$", // SUBSCRIPT TWO
  1336. "\u2083":"$_{3}$", // SUBSCRIPT THREE
  1337. "\u2084":"$_{4}$", // SUBSCRIPT FOUR
  1338. "\u2085":"$_{5}$", // SUBSCRIPT FIVE
  1339. "\u2086":"$_{6}$", // SUBSCRIPT SIX
  1340. "\u2087":"$_{7}$", // SUBSCRIPT SEVEN
  1341. "\u2088":"$_{8}$", // SUBSCRIPT EIGHT
  1342. "\u2089":"$_{9}$", // SUBSCRIPT NINE
  1343. "\u208A":"$_{+}$", // SUBSCRIPT PLUS SIGN
  1344. "\u208B":"$_{-}$", // SUBSCRIPT MINUS
  1345. "\u208C":"$_{=}$", // SUBSCRIPT EQUALS SIGN
  1346. "\u208D":"$_{(}$", // SUBSCRIPT LEFT PARENTHESIS
  1347. "\u208E":"$_{)}$", // SUBSCRIPT RIGHT PARENTHESIS
  1348. "\u20AC":"{\\texteuro}", // EURO SIGN
  1349. "\u2100":"a/c", // ACCOUNT OF
  1350. "\u2101":"a/s", // ADDRESSED TO THE SUBJECT
  1351. "\u2103":"{\\textcelsius}", // DEGREE CELSIUS
  1352. "\u2105":"c/o", // CARE OF
  1353. "\u2106":"c/u", // CADA UNA
  1354. "\u2109":"F", // DEGREE FAHRENHEIT
  1355. "\u2113":"l", // SCRIPT SMALL L
  1356. "\u2116":"{\\textnumero}", // NUMERO SIGN
  1357. "\u2117":"{\\textcircledP}", // SOUND RECORDING COPYRIGHT
  1358. "\u2120":"{\\textservicemark}", // SERVICE MARK
  1359. "\u2121":"TEL", // TELEPHONE SIGN
  1360. "\u2122":"{\\texttrademark}", // TRADE MARK SIGN
  1361. "\u2126":"{\\textohm}", // OHM SIGN
  1362. "\u212A":"K", // KELVIN SIGN
  1363. "\u212B":"A", // ANGSTROM SIGN
  1364. "\u212E":"{\\textestimated}", // ESTIMATED SYMBOL
  1365. "\u2153":" 1/3", // VULGAR FRACTION ONE THIRD
  1366. "\u2154":" 2/3", // VULGAR FRACTION TWO THIRDS
  1367. "\u2155":" 1/5", // VULGAR FRACTION ONE FIFTH
  1368. "\u2156":" 2/5", // VULGAR FRACTION TWO FIFTHS
  1369. "\u2157":" 3/5", // VULGAR FRACTION THREE FIFTHS
  1370. "\u2158":" 4/5", // VULGAR FRACTION FOUR FIFTHS
  1371. "\u2159":" 1/6", // VULGAR FRACTION ONE SIXTH
  1372. "\u215A":" 5/6", // VULGAR FRACTION FIVE SIXTHS
  1373. "\u215B":" 1/8", // VULGAR FRACTION ONE EIGHTH
  1374. "\u215C":" 3/8", // VULGAR FRACTION THREE EIGHTHS
  1375. "\u215D":" 5/8", // VULGAR FRACTION FIVE EIGHTHS
  1376. "\u215E":" 7/8", // VULGAR FRACTION SEVEN EIGHTHS
  1377. "\u215F":" 1/", // FRACTION NUMERATOR ONE
  1378. "\u2160":"I", // ROMAN NUMERAL ONE
  1379. "\u2161":"II", // ROMAN NUMERAL TWO
  1380. "\u2162":"III", // ROMAN NUMERAL THREE
  1381. "\u2163":"IV", // ROMAN NUMERAL FOUR
  1382. "\u2164":"V", // ROMAN NUMERAL FIVE
  1383. "\u2165":"VI", // ROMAN NUMERAL SIX
  1384. "\u2166":"VII", // ROMAN NUMERAL SEVEN
  1385. "\u2167":"VIII", // ROMAN NUMERAL EIGHT
  1386. "\u2168":"IX", // ROMAN NUMERAL NINE
  1387. "\u2169":"X", // ROMAN NUMERAL TEN
  1388. "\u216A":"XI", // ROMAN NUMERAL ELEVEN
  1389. "\u216B":"XII", // ROMAN NUMERAL TWELVE
  1390. "\u216C":"L", // ROMAN NUMERAL FIFTY
  1391. "\u216D":"C", // ROMAN NUMERAL ONE HUNDRED
  1392. "\u216E":"D", // ROMAN NUMERAL FIVE HUNDRED
  1393. "\u216F":"M", // ROMAN NUMERAL ONE THOUSAND
  1394. "\u2170":"i", // SMALL ROMAN NUMERAL ONE
  1395. "\u2171":"ii", // SMALL ROMAN NUMERAL TWO
  1396. "\u2172":"iii", // SMALL ROMAN NUMERAL THREE
  1397. "\u2173":"iv", // SMALL ROMAN NUMERAL FOUR
  1398. "\u2174":"v", // SMALL ROMAN NUMERAL FIVE
  1399. "\u2175":"vi", // SMALL ROMAN NUMERAL SIX
  1400. "\u2176":"vii", // SMALL ROMAN NUMERAL SEVEN
  1401. "\u2177":"viii", // SMALL ROMAN NUMERAL EIGHT
  1402. "\u2178":"ix", // SMALL ROMAN NUMERAL NINE
  1403. "\u2179":"x", // SMALL ROMAN NUMERAL TEN
  1404. "\u217A":"xi", // SMALL ROMAN NUMERAL ELEVEN
  1405. "\u217B":"xii", // SMALL ROMAN NUMERAL TWELVE
  1406. "\u217C":"l", // SMALL ROMAN NUMERAL FIFTY
  1407. "\u217D":"c", // SMALL ROMAN NUMERAL ONE HUNDRED
  1408. "\u217E":"d", // SMALL ROMAN NUMERAL FIVE HUNDRED
  1409. "\u217F":"m", // SMALL ROMAN NUMERAL ONE THOUSAND…

Large files files are truncated, but you can click here to view the full file