/*
* Author = Philip Cooksey
* Edited = September 2018
* Website = https://github.com/pcooksey/bibtex-js
* Credit = Henrik Mühe
*
* Issues:
* no comment handling within strings
* no string concatenation
* no variable values yet
* Grammar implemented here:
* bibtex -> (string | preamble | comment | entry)*;
* string -> '@STRING' '{' key_equals_value '}';
* preamble -> '@PREAMBLE' '{' value '}';
* comment -> '@COMMENT' '{' value '}';
* entry -> '@' key '{' key ',' key_value_list '}';
* key_value_list -> key_equals_value (',' key_equals_value)*;
* key_equals_value -> key '=' value;
* value -> value_quotes | value_braces | key;
* value_quotes -> '"' .*? '"'; // not quite
* value_braces -> '{' .*? '"'; // not quite
*
*/
function BibtexParser() {
this.pos = 0;
this.input = "";
this.entries = {};
this.strings = {
JAN: "January",
FEB: "February",
MAR: "March",
APR: "April",
MAY: "May",
JUN: "June",
JUL: "July",
AUG: "August",
SEP: "September",
OCT: "October",
NOV: "November",
DEC: "December"
};
this.currentKey = "";
this.rawCurrentKey = "";
this.currentEntry = "";
this.setInput = function(t) {
this.input = t;
}
this.getEntries = function() {
return this.entries;
}
this.getBibTexRaw = function() {
return this.bibtexraw;
}
this.errorThrown = function(str) {
$("#bibtex_errors").html(str);
}
this.isWhitespace = function(s) {
return (s == ' ' || s == '\r' || s == '\t' || s == '\n');
}
this.match = function(s) {
this.skipWhitespace();
if (this.input.substring(this.pos, this.pos + s.length) == s) {
this.pos += s.length;
} else {
throw "Token mismatch, expected " + s + ", found " + this.input.substring(this.pos);
}
this.skipWhitespace();
}
this.tryMatch = function(s) {
this.skipWhitespace();
if (this.input.substring(this.pos, this.pos + s.length) == s) {
return true;
} else {
return false;
}
this.skipWhitespace();
}
this.skipWhitespace = function() {
while (this.isWhitespace(this.input[this.pos])) {
this.pos++;
}
if (this.input[this.pos] == "%") {
while (this.input[this.pos] != "\n") {
this.pos++;
}
this.skipWhitespace();
}
}
this.value_braces = function() {
var bracecount = 0;
this.match("{");
var start = this.pos;
while (true) {
if (this.input[this.pos] == '}' && this.input[this.pos - 1] != '\\') {
if (bracecount > 0) {
bracecount--;
} else {
var end = this.pos;
this.match("}");
return this.input.substring(start, end);
}
} else if (this.input[this.pos] == '{') {
bracecount++;
} else if (this.pos == this.input.length - 1) {
throw "Unterminated value";
}
this.pos++;
}
}
this.value_quotes = function() {
var bracecount = 0;
this.match('"');
var start = this.pos;
while (true) {
if (this.input[this.pos] == '"' && this.input[this.pos - 1] != '\\' && bracecount == 0) {
var end = this.pos;
this.match('"');
return this.input.substring(start, end);
} else if (this.input[this.pos] == '{') {
bracecount++;
} else if (this.input[this.pos] == '}') {
if (bracecount > 0) {
bracecount--;
}
} else if (this.pos == this.input.length - 1) {
throw "Unterminated value:" + this.input.substring(start);
}
this.pos++;
}
}
this.single_value = function() {
var start = this.pos;
if (this.tryMatch("{")) {
return this.value_braces();
} else if (this.tryMatch('"')) {
return this.value_quotes();
} else {
var k = this.key();
if (this.strings[k.toUpperCase()]) {
return this.strings[k];
} else if (k.match("^[0-9]+$")) {
return k;
} else {
throw "Value expected:" + this.input.substring(start);
}
}
}
this.value = function() {
var values = [];
values.push(this.single_value());
while (this.tryMatch("#")) {
this.match("#");
values.push(this.single_value());
}
return values.join("");
}
this.key = function() {
var start = this.pos;
while (true) {
if (this.pos == this.input.length) {
throw "Runaway key";
}
if (this.input[this.pos].match("[a-zA-Z0-9_:?\\./'\\+\\-\\*]")) {
this.pos++
} else {
this.rawCurrentKey = this.input.substring(start, this.pos);
return this.rawCurrentKey.toUpperCase();
}
}
}
this.key_equals_value = function() {
var key = this.key();
if (this.tryMatch("=")) {
this.match("=");
var val = this.value();
return [key, val];
} else {
throw "... = value expected, equals sign missing:" + this.input.substring(this.pos);
}
}
this.key_value_list = function() {
var kv = this.key_equals_value();
this.entries[this.currentEntry][kv[0]] = kv[1];
while (this.tryMatch(",")) {
this.match(",");
// fixes problems with commas at the end of a list
if (this.tryMatch("}") || this.tryMatch(")")) {
break;
}
kv = this.key_equals_value();
this.entries[this.currentEntry][kv[0]] = kv[1];
}
}
this.entry_body = function(directive) {
this.currentEntry = this.key();
this.entries[this.currentEntry] = new Object();
this.entries[this.currentEntry]["BIBTEXKEY"] = this.rawCurrentKey;
if (directive == "@INCOLLECTION") {
this.entries[this.currentEntry]["BIBTEXTYPE"] = "book chapter";
} else if (directive == "@INPROCEEDINGS") {
this.entries[this.currentEntry]["BIBTEXTYPE"] = "conference, workshop";
} else if (directive == "@ARTICLE") {
this.entries[this.currentEntry]["BIBTEXTYPE"] = "journal";
} else if (directive == "@TECHREPORT") {
this.entries[this.currentEntry]["BIBTEXTYPE"] = "technical report";
}
this.entries[this.currentEntry]["BIBTEXTYPEKEY"] = directive.substr(1);
this.match(",");
this.key_value_list();
}
this.directive = function() {
this.match("@");
return "@" + this.key();
}
this.string = function() {
var kv = this.key_equals_value();
this.strings[kv[0].toUpperCase()] = kv[1];
}
this.preamble = function() {
this.value();
}
this.comment = function() {
this.pos = this.input.indexOf("}", this.pos);
}
this.entry = function(directive) {
this.entry_body(directive);
}
this.bibtex = function() {
var start = 0;
var end = 0;
while (this.tryMatch("@")) {
start = this.pos;
var d = this.directive().toUpperCase();
if (this.tryMatch("{")) {
this.match("{");
} else {
this.match("(");
}
if (d == "@STRING") {
this.string();
} else if (d == "@PREAMBLE") {
this.preamble();
} else if (d == "@COMMENT") {
this.comment();
} else {
this.entry(d);
}
end = this.pos + 1;
if (this.tryMatch("}")) {
this.match("}");
} else {
this.match(")");
}
if (this.tryMatch(",")) {
this.match(",");
}
// In case there is extra stuff in between entries
this.pos = end + this.input.substring(end, this.input.length).indexOf("@");
this.entries[this.currentEntry]["BIBTEXRAW"] = this.input.substring(start, end);
}
}
}
function BibtexDisplay() {
this.invert = function(obj) {
var new_obj = {};
for (var prop in obj) {
if (obj.hasOwnProperty(prop)) {
new_obj[obj[prop]] = prop;
}
}
return new_obj;
}
function isSymbol(str) {
return str.length === 1 && str.test(/[\W]/i);
}
//Regex Searchs used by fixValue in proper order
this.regExps = [];
this.regExps.push(new RegExp("\{\\\\\\W*\\w+\}")); // 1 {\[]}
this.regExps.push(new RegExp("\\\\\\W*\{\\w+\}")); // 2 \[]{\[]}
this.regExps.push(new RegExp("\\\\\\W*\\w+\\s")); // 3 \[]
this.regExps.push(new RegExp("\\\\\\W*\\w+")); // 4 \[]
this.regExps.push(new RegExp("\\\\(?![:\\\\\])\\W{1}")); // 5
this.fixValue = function(value) {
do {
var removeBrackets = value.match(/^\{(.*?)\}$/g, '$1');
if (removeBrackets) {
value = value.replace(/^\{(.*?)\}$/g, '$1');
}
} while (removeBrackets);
// Working on a more efficient way of processing the latex
var index = value.indexOf("\\");
if (index > -1) {
for (var exp in this.regExps) {
do {
var str = value.match(this.regExps[exp]);
var key = (str) ? str[0] : "";
if (str) {
if (typeof(latex_to_unicode[key]) != "undefined") {
value = value.replace(key, latex_to_unicode[key]);
} else {
var newkey = key.replace(new RegExp("(\\w)"), '{$1}')
if (typeof(latex_to_unicode[newkey]) != "undefined") {
value = value.replace(key, latex_to_unicode[newkey]);
} else {
str = "";
}
}
} else {
str = "";
}
} while (str.length);
}
}
value = value.replace(/[\{|\}]/g, '');
return value;
}
this.getName = function(array) {
// First, Junior, Von, Last, First Initals
var name = ["", "", "", "", ""];
// check how many elements in array 1, 2, or 3
switch (array.length) {
case 1: {
// Split by spaces keeping {names names} together
var words = array[0].split(/\ \s?(?![^\{]*\})/);
// Get first name
var index = 0;
for (; index < words.length - 1; index++) {
var space = (index > 0) ? " " : "";
if (words[index][0] == '{') {
if (words[index][1] == '\\') {
words[index] = this.fixValue(words[index]);
// Test below
} else {
name[0] += space + this.fixValue(words[index]);
continue;
}
}
if (words[index][0] == words[index][0].toUpperCase()) {
name[0] += space + this.fixValue(words[index]);
} else {
break; //von part
}
}
// Get von part forward look for last non uppercase
var lastVon = index;
for (var temp = index; temp < words.length - 1; temp++) {
if (words[temp][0] == words[temp][0].toLowerCase()) {
lastVon = temp;
}
}
for (; index <= lastVon && index < words.length - 1; index++) {
var space = (name[2] != "") ? " " : "";
name[2] += space + this.fixValue(words[index]);
}
// Get last name
for (; index < words.length; index++) {
var space = (name[3] != "") ? " " : "";
name[3] += space + this.fixValue(words[index]);
}
}
break;
case 2:
case 3: {
var arrayIndex = 0;
// Split by spaces keeping {names names} together
var words = array[arrayIndex].split(/\ \s?(?![^\{]*\})/);
var index = 0;
// Get von part forward look for last non uppercase
var lastVon = -1;
for (var temp = index; temp < words.length - 1; temp++) {
if (words[temp][0] == words[temp][0].toLowerCase()) {
lastVon = temp;
}
}
for (; index <= lastVon && index < words.length - 1; index++) {
var space = (name[2] != "") ? " " : "";
name[2] += space + this.fixValue(words[index]);
}
// Get last name
for (; index < words.length; index++) {
var space = (name[3] != "") ? " " : "";
name[3] += space + this.fixValue(words[index]);
}
arrayIndex++;
// Check if there is a Jr. part
if (array.length == 3) {
name[1] = this.fixValue(array[arrayIndex]);
arrayIndex++;
}
// Get first name
name[0] = this.fixValue(array[arrayIndex]);
}
break;
default:
console.log("Processed author incorrectly!");
return name;
}
if (name[0] != "") {
name[4] = name[0].split(" ").map((s) => s.substring(0, 1).toUpperCase()).join(". ") + ".";
}
return name;
}
var Format = Object.freeze({
"FIRST": 0,
"JUNIOR": 1,
"VON": 2,
"LAST": 3,
"FIRST_INITIAL": 4
});
this.displayAuthor = function(string, format) {
string = string.replace(/[ ]*[\n\t][ ]*/g, " ");
string = string.replace(/[ ]+/g, " ");
// Split string by 'and' keeping {words and words} together
var arrayString = string.split(new RegExp(/\s+and\s+?(?![^\{]*\})/));
// Get the max amount of authors to print
var searchLength = arrayString.length;
if (format.attr("max")) {
searchLength = Math.min(format.attr("max"), searchLength);
}
// Get conjunction if set in author
conjunction = format.attr('conjunction') ? format.attr('conjunction') : ', and';
conjunction = "" + conjunction + "";
var newString = "";
for (i = 0; i < searchLength; i++) {
// Split string by ',' keeping {words, and words} together
var name = this.getName(arrayString[i].split(/\,\s?(?![^\{]*\})/));
var author = format.clone();
var fullName = $.grep(name.slice(0, 4), Boolean).join(" ");
author.attr('class', fullName);
var textBefore = false;
for (j = 0, ele = author.find("span:not(a)"), len = ele.length; j < len; ++j) {
var index = Format[ele[j].getAttribute("class").toUpperCase()];
// Check if value is empty
if (name[index] != "") {
if (ele[j].hasAttribute("bibtex-js-rif") && !textBefore) {
ele[j].textContent = name[index];
} else {
ele[j].textContent = ele[j].textContent + name[index];
}
textBefore = true;
} else {
ele[j].remove();
}
}
if (i == 0) {
newString += author[0].outerHTML;
} else if (i + 1 >= arrayString.length) {
newString += conjunction + " " + author[0].outerHTML;
} else {
newString += ", " + author[0].outerHTML;
}
}
// Checking if et al. must be added
if (searchLength != arrayString.length) {
if (searchLength > 1) {
newString += ",";
}
newString += " et al.";
}
return newString;
}
this.createTemplate = function(entry, output) {
// Check if bibtex keys are limiting output (bibtexkeys="key1|key2|...|keyN")
if (output[0].attributes.length > 1) {
for (var i = 0, attrs = output[0].attributes, n = attrs.length; i < n; ++i) {
var name = attrs[i].nodeName.toUpperCase();
var value = attrs[i].nodeValue;
if (name in entry) {
if (!entry[name].match(value))
return null;
}
}
}
// find template
var tpl = $(".bibtex_template").clone().removeClass('bibtex_template');
// find all keys in the entry
var keys = [];
for (var key in entry) {
keys.push(key.toUpperCase());
}
// Regex
var orRegExp = new RegExp('\\s*[\\|\\|]\\s*', "gi"); // split by "||"
var eqRegExp = new RegExp('\\s*==\\s*', "gi"); // split by "=="
// find all ifs and check them
var removed = false;
do {
// find next if
var conds = tpl.find(".if");
if (conds.length == 0) {
break;
}
// check if
var cond = conds.first();
cond.removeClass("if");
var keepIf = false;
var classList = cond.attr('class').split(orRegExp);
var self = this;
$.each(classList, function(index, cls) {
var equation = cls.split(eqRegExp);
if (equation.length == 2) {
if (keys.indexOf(equation[0].toUpperCase()) > 0 &&
self.fixValue(entry[equation[0].toUpperCase()]) == equation[1]) {
keepIf |= true;
}
} else {
if (cls[0] == "!" &&
keys.indexOf(cls.substring(1, cls.length).toUpperCase()) < 0) {
keepIf |= true;
} else if (keys.indexOf(cls.toUpperCase()) > 0) {
keepIf |= true;
}
}
cond.removeClass(cls);
});
// remove false ifs
if (!keepIf) {
cond.remove();
}
} while (true);
tpl.find('.bibtexVar').each(function() {
var key = $(this).attr("extra").toUpperCase();
var regEx = new RegExp('\\+' + key + '\\+', "gi");
var entryValue = entry[key];
if ($(this)[0].hasAttribute("bibtexjs-css-escape")) {
entryValue = entryValue.replace(/[\!\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\[\\\]\^\`\{\|\}\~ ]/g, '\\$&');
}
$.each(this.attributes, function(i, attrib) {
var value = attrib.value;
value = value.replace(regEx, entryValue);
attrib.value = value;
});
});
// fill in remaining fields
for (var index in keys) {
var key = keys[index];
var value = entry[key] || "";
// Fill out bibtex raw and continue
if (key == "BIBTEXRAW") {
tpl.find("." + key.toLowerCase()).html(value);
continue;
}
if (key == "AUTHOR") {
var format = tpl.find("span:not(a)." + key.toLowerCase());
if (format.length)
value = this.displayAuthor(value, format);
} else if (key == "PAGES") {
value = value.replace("--", "-");
} else if (key == "DATE") {
value = moment(value).format("MMM. YYYY");
} else if (key == "URL") {
value = value.replace(/\\url/g, '');
value = this.fixValue(value);
} else {
value = this.fixValue(value);
}
tpl.find("span:not(a)." + key.toLowerCase()).html(value);
tpl.find("a." + key.toLowerCase()).each(function() {
if (!$(this).attr("href")) {
$(this).attr("href", value);
}
});
}
tpl.addClass("bibtexentry");
return tpl;
}
this.createArray = function(entries) {
var entriesArray = [];
for (var entryKey in entries) {
entriesArray.push(entries[entryKey]);
}
return entriesArray;
}
this.sortArray = function(array, key, rule, type) {
var keyUpper = key.toUpperCase();
array = array.sort(function(a, b) {
var aValue = "",
bValue = "";
// Need to check if values exist
aValue = (keyUpper in a) ? a[keyUpper] : "";
bValue = (keyUpper in b) ? b[keyUpper] : "";
// `year` and `date` fall back to each other.
if (keyUpper == "DATE") {
var getDateFromYearMonth = x => {
return ((("MONTH" in x) ? x["MONTH"] : "Jan") +
" 1, " +
(("YEAR" in x) ? x["YEAR"] : "1900"));
};
if (!aValue) aValue = getDateFromYearMonth(a);
if (!bValue) bValue = getDateFromYearMonth(b);
} else if (keyUpper == "YEAR") {
var getYearFromDate = x => {
if ("DATE" in x) {
return moment(x["DATE"]).format("YYYY");
}
return "";
}
if (!aValue) aValue = getYearFromDate(a);
if (!bValue) bValue = getYearFromDate(b);
}
switch (rule.toUpperCase()) {
case "DESC":
//Values remain the same
break;
case "ASC":
//Just swaping the values
var tmp = bValue;
bValue = aValue;
aValue = tmp;
break;
default:
return 0;
break;
}
switch (type.toLowerCase()) {
case "string":
return bValue.toUpperCase().localeCompare(aValue.toUpperCase());
break;
case "number":
return parseInt(bValue) - parseInt(aValue);
break;
case "date":
return new Date(bValue) - new Date(aValue);
break;
default:
return 0;
break;
}
});
return array;
}
this.createStructure = function(structure, output, entries, level) {
var MissingGroup = "Other Publications";
//Used during the search
level = level || 0;
var struct = structure.clone().removeClass('bibtex_structure');
var groupChild = struct.children(".group");
var sectionsChild = struct.children(".sections");
var sortChild = struct.children(".sort");
if (groupChild.length) {
var group = groupChild.first();
var groupName = group.attr('class').split(" ")[1].toUpperCase();
var rule = group.attr('extra').split(" ")[0];
var type = group.attr('extra').split(" ")[1];
//Sort the array based on group rules
var sortedArray = this.sortArray(entries, groupName, rule, type);
// Get all the unique values for the groups
var values = [];
$.each(sortedArray, function(i, object) {
if (groupName in object && $.inArray(object[groupName], values) === -1) {
values.push(object[groupName]);
return;
}
});
values.push(MissingGroup); //This is for checking none grouped publications
//Get the bibtex topics html here.
var topics = $(".bibtex_topics");
// Iterate through the values and recurively call this function
globalStruct = $('
');
for (val in values) {
//Starting to create the page
var newStruct = struct.clone();
var groupNameValue = values[val];
//Add the header for the group
var header = newStruct.children("." + groupName.toLowerCase()).first().find(".title");
if (header.length) {
header.prepend(this.fixValue(groupNameValue));
header.attr("id", this.fixValue(groupNameValue));
} else {
newStruct.children("." + groupName.toLowerCase()).first().prepend("" + this.fixValue(groupNameValue) + "");
}
//Divide the array into group with groupNameValue
splicedArray = $.grep(sortedArray, function(object, i) {
if (groupNameValue == MissingGroup) {
return (typeof object[groupName] === "undefined") ? true : false;
} else {
return object[groupName] == groupNameValue;
}
});
if (splicedArray.length) {
//Add the topic value to the topics structure if it exists on the page
if (topics.length && level == 0) {
topics.append(" - " + groupNameValue + " ");
}
// Get back the struct to add to the page
var tempStruct = this.createStructure(groupChild.clone(), output, splicedArray, level + 1);
if (groupChild.children(".group").length) {
nextGroupName = "." + groupChild.children(".group").attr('class').split(' ').join('.');
newStruct.find(nextGroupName).replaceWith(tempStruct.find(nextGroupName));
} else {
newStruct.find(".templates").append(tempStruct.find(".templates").html());
}
if (level == 0) {
output.append(newStruct);
} else {
globalStruct.append(newStruct);
}
}
}
if (level == 0) {
return output;
} else {
return globalStruct;
}
} else if (sectionsChild.length) {
var values = [],
section = [],
toRemove = [];
// Get all the unique values for the sections
var sectionbibtexkey = sectionsChild.first().attr('class').split(" ")[1].toUpperCase();
$('.section', '.sections').each(function(i, object) {
values.push($(this).attr('class').split(" ")[1].toUpperCase());
section.push($(this));
});
//Get the bibtex topics html here.
var topics = $(".bibtex_topics");
// Iterate through the values and recurively call this function
globalStruct = $('');
//Starting to create the page
var newStruct = struct.clone();
for (val in values) {
var sectionNameValue = values[val];
var re = new RegExp(sectionNameValue);
//Divide the array into group with sectionNameValue
splicedArray = $.grep(entries, function(object, i) {
return re.test(object[sectionbibtexkey]);
});
if (splicedArray.length) {
//Add the topic value to the topics structure if it exists on the page
if (topics.length && level == 0) {
var sectionNameTitle = section[val].children().first().text();
topics.append(" - " + sectionNameTitle + " ");
}
// Get back the struct to add to the page
var tempStruct = this.createStructure(section[val].clone(), output, splicedArray, level + 1);
if (groupChild.children(".group").length) {
nextGroupName = "." + groupChild.children(".group").attr('class').split(' ').join('.');
newStruct.find(nextGroupName).replaceWith(tempStruct.find(nextGroupName));
} else {
newStruct.find(".templates").eq(val).append(tempStruct.find(".templates").html());
}
if (level == 0) {
output.append(newStruct);
} else {
globalStruct.append(newStruct);
}
} else {
toRemove.push(newStruct.find(".section").eq(val));
}
}
for (val in toRemove) {
toRemove[val].remove();
}
if (level == 0) {
return output;
} else {
return globalStruct;
}
} else if (sortChild.length) {
var sortName = sortChild.attr('class').split(" ")[1].toUpperCase();
var rule = sortChild.first().attr('extra').split(" ")[0];
var type = sortChild.first().attr('extra').split(" ")[1];
var sort = structure.children(".sort").first().clone();
//Sort the array based on sort rules
var sortedArray = this.sortArray(entries, sortName, rule, type);
if (level == 0) {
output.append(this.createStructure(sortChild, output, sortedArray, level + 1));
} else {
return this.createStructure(sortChild, output, sortedArray, level + 1);
}
} else {
// iterate over bibTeX entries and add them to template
for (var entryKey in entries) {
var entry = entries[entryKey];
// Checking if web is set to visible
if (!entry["WEB"] || entry["WEB"].toUpperCase() != "NO") {
var tpl = this.createTemplate(entry, output);
// Check if template was created
if (tpl) {
structure.find(".templates").append(tpl);
tpl.show();
if (tpl.attr("callback")) {
var callback = new Function('bibtexentry', tpl.attr("callback"));
callback(tpl[0]);
}
}
}
}
return structure;
}
}
this.displayBibtex = function(input, output) {
// parse bibtex input
var b = new BibtexParser();
b.setInput(input);
try {
b.bibtex();
} catch (e) {
b.errorThrown(e);
console.error(e);
}
var entries = b.getEntries();
// save old entries to remove them later
var old = output.find("*");
var structure = $(".bibtex_structure").clone();
// If structure exists we need to do more complicated sorting with entries
if (structure.length) {
// Create array for sorting
var entriesArray = this.createArray(entries);
this.createStructure(structure, output, entriesArray);
} else {
// iterate over bibTeX entries
for (var entryKey in entries) {
var entry = entries[entryKey];
tpl = this.createTemplate(entry, output);
// Check if template was created
if (tpl) {
output.append(tpl);
tpl.show();
if (tpl.attr("callback")) {
var callback = new Function('bibtexentry', tpl.attr("callback"));
callback(tpl[0]);
}
}
}
}
// remove old entries
old.remove();
}
}
function bibtex_js_draw() {
$(".bibtex_template").hide();
//Gets the BibTex files and adds them together
var bibstring = "";
var requests = [];
if ($("#bibtex_input").length) {
bibstring += $("#bibtex_input").val();
}
// Create request for bibtex files
$('bibtex').each(function(index, value) {
var request = $.ajax({
url: $(this).attr('src'),
dataType: "text"
})
.done((data) => bibstring += data)
.fail((request, status, error) => console.error(error))
requests.push(request);
});
// Add default author format if it doesn't exist
var authorFormat = $(".bibtex_template").find("span:not(a).author");
if (authorFormat.length && !authorFormat.find("span:not(a)").length) {
authorFormat.append($("").attr("class", "first"));
authorFormat.append($("").attr("class", "von")
.attr("bibtex-js-rif", "").text(" "));
authorFormat.append($("").attr("class", "last")
.attr("bibtex-js-rif", "").text(" "));
authorFormat.append($("").attr("class", "junior").text(", "));
}
// Executed on completion of last outstanding ajax call
$.when.apply($, requests).then(function() {
// Check if we have a bibtex_display id or classes
if ($("#bibtex_display").length) {
var bibtex_display = $("#bibtex_display");
(new BibtexDisplay()).displayBibtex(bibstring, bibtex_display);
if (bibtex_display.attr("callback")) {
var callback = new Function('bibtex_display', bibtex_display.attr("callback"));
callback(bibtex_display[0]);
}
} else if ($(".bibtex_display").length) {
// Loop through all bibtex_displays on the page
$(".bibtex_display").each(function(index) {
// ($this) is the class node output for the bitex entries
(new BibtexDisplay()).displayBibtex(bibstring, $(this));
if ($(this).attr("callback")) {
var callback = new Function('bibtex_display', $(this).attr("callback"));
callback($(this)[0]);
}
});
}
// Remove elements from html that are not needed to display
$(".bibtex_structure").remove();
loadExtras();
});
}
/**
BibTex Searcher is used with input form
*/
function BibTeXSearcher() {
this.inputArray = new Array("");
this.inputLength = 0;
this.setInputArray = function(val) {
this.inputArray = val;
this.inputLength = val.length;
}
this.getStringName = function(string) {
var start_pos = string.indexOf('@') + 1;
var end_pos = string.indexOf('[', start_pos);
var array = [];
if (end_pos == -1) {
array[0] = string.substring(start_pos, string.length);
} else {
array[0] = string.substring(start_pos, end_pos);
end_pos2 = string.indexOf(']', start_pos);
array[1] = string.substring(end_pos + 1, end_pos2);
}
return array;
}
this.checkEntry = function(entry, word) {
var found = false;
if (word[0] != "@") {
entry.find("span:not(.noread)").each(
function() {
if ($(this).text().search(new RegExp(word, "i")) > -1 &&
entry.is(":visible")) {
found = true;
return false; //Break out of loop
}
});
} else {
//This search version is for more specific searchs using the @name[parameter]=value
var strings = word.split("=");
var arrayStr = this.getStringName(strings[0]);
if (arrayStr.length < 2) {
entry.find("span:not(.noread)." + arrayStr[0]).each(
function() {
if ($(this).text().search(new RegExp(strings[1], "i")) > -1 &&
entry.is(":visible")) {
found = true;
return false; //Break out of loop
}
});
} else {
switch (arrayStr[1]) {
case "first":
entry.find("span:not(.noread)." + arrayStr[0]).each(
function() {
arrayString = $(this).text().split(new RegExp(",[\\s]+and[\\s]+|,[\\s]+"));
if (strings[1] == arrayString[0] && entry.is(":visible")) {
found = true;
return false; //Break out of loop
}
});
break;
}
}
}
return found;
}
this.unhideAll = function() {
$("div#bibtex_display, div.bibtex_display").children().each(
function() {
$(this).show();
$(this).find(".bibtexentry").each(
function() {
$(this).show();
});
});
}
this.hideEntry = function(word) {
var funcCaller = this;
var container = $("div#bibtex_display, div.bibtex_display").children();
// No bibtex_structure search
if (container.first().hasClass("bibtexentry")) {
container.each(
function() {
if (!funcCaller.checkEntry($(this), word)) {
$(this).hide();
}
});
} else {
// There is a bibtex_structure
container.each(
function() {
var shouldHide = true;
$(this).find(".bibtexentry:visible").each(
function() {
if (!funcCaller.checkEntry($(this), word)) {
$(this).hide();
} else {
shouldHide = false;
}
});
// Hides outside div
if (shouldHide) {
$(this).hide();
}
});
}
}
this.searcher = function(input, needToRestart) {
needToRestart = typeof needToRestart !== 'undefined' ? needToRestart : false;
var string = input;
if (string.length) {
var splitInput = string.split("%");
//If input is less than restart
if (this.inputLength > splitInput.length || this.inputLength == 0) {
needToRestart = true;
}
//If last string reduced in size than restart
else if (this.inputArray[this.inputArray.length - 1].length >
splitInput[splitInput.length - 1].length) {
needToRestart = true;
}
//If earlier words changed than restart
else {
for (var i = 0; i < this.inputArray.length - 1; i++) {
if (this.inputArray[i] != splitInput[i]) {
needToRestart = true;
break;
}
}
}
if (needToRestart) {
this.unhideAll();
for (var word in splitInput) {
this.hideEntry(splitInput[word]);
}
} else {
this.hideEntry(splitInput[splitInput.length - 1]);
}
this.setInputArray(splitInput);
} else {
this.unhideAll();
}
}
}
function createWebPage(defaultTemplate) {
// draw bibtex when loaded
$(document).ready(function() {
// check for template, add default
if ($(".bibtex_template").length == 0) {
$("body").append(defaultTemplate);
}
bibtex_js_draw();
});
}
function loadExtras() {
BibTeXSearcherClass = new BibTeXSearcher();
// Generate all search lists
$("select").each(function() {
for (var i = 0, l = this.classList.length; i < l; ++i) {
var checkRegEx = this.classList[i].match(/bibtex_generate_(.*)/);
if (checkRegEx) {
var field = checkRegEx[1];
generateList($(this), field);
}
}
});
localStorage.removeItem("customerDatabase");
if (!localStorage.searcher) {
localStorage.searcher = new Object();
}
//Resets selects when back button is used
$("select").each(function() {
if (localStorage.getItem($(this).attr("id"))) {
$(this).val(JSON.parse(localStorage.getItem($(this).attr("id"))));
}
});
$(".bibtex_search").each(function(i, obj) {
$(this).on('change', function(e) {
combineSearcher(BibTeXSearcherClass, true);
localStorage.setItem($(this).attr("id"), JSON.stringify($(this).val()));
});
$(this).keyup(function() {
combineSearcher(BibTeXSearcherClass);
});
if (obj.length > 0 && $(this).val() != "") {
combineSearcher(BibTeXSearcherClass, true);
}
});
}
function combineSearcher(searcherClass, needToRestart) {
needToRestart = typeof needToRestart !== 'undefined' ? needToRestart : false;
var string = "";
$("select.bibtex_search").each(function(i, obj) {
var front = "";
if (obj.hasAttribute("search"))
front = "@" + $(this).attr("search");
if (obj.hasAttribute("extra")) {
front += "[" + $(this).attr("extra") + "]=";
} else {
if (front != "") {
front += "=";
}
}
if ($(this).val() != "") {
string += "%" + front + $(this).val();
}
});
$("input.bibtex_search").each(function(i, obj) {
if ($(this).val() != "") {
string += "%" + $(this).val().split(' ').join('%');
}
});
searcherClass.searcher(string, needToRestart);
}
function generateList(object, bibtexField) {
var map = new Object();
var displayTuples = [];
if (bibtexField == "author") {
$(".bibtexentry span.author").each(function(i, obj) {
authors = $(this).children("span:not(.bibtex_js_conjunction)");
authorLength = authors.length;
if (object.attr("extra") == "first") {
authorLength = 1;
}
for (i = 0; i < authorLength; i++) {
if (authors[i].innerText in map) {
map[authors[i].innerText][0] += 1;
} else {
var strName = "";
var nameSplit = authors[i].className.split(" ");
var lastName = nameSplit.pop();
if (nameSplit.length >= 1) {
strName = lastName + ", " + nameSplit.join(" ");
} else {
strName = lastName;
}
map[authors[i].innerText] = [1, strName, lastName];
}
}
});
for (var key in map) {
var lastName = map[key][2];
displayTuples.push([lastName.toLowerCase(), //Last name
key, // Full name
map[key][1], //Print
map[key][0]
]); //Count
}
} else {
$(".bibtexentry span." + bibtexField).each(function(i, obj) {
arrayString = [$(this).text()];
if (object[0].hasAttribute("bibtex_split_by")) {
arrayString = arrayString[0].split(object.attr("bibtex_split_by"));
}
for (i = 0; i < arrayString.length; ++i) {
if (arrayString[i] in map) {
map[arrayString[i]] += 1;
} else {
map[arrayString[i]] = 1;
}
}
});
for (var key in map) {
displayTuples.push([key, key, key, map[key]]);
}
}
var options = {};
var optionStr = object.attr("bibtex_sort_options");
if (optionStr !== undefined) {
var properties = optionStr.split(",").map(item => item.trim());
properties.forEach(function(property) {
var tup = property.split(':');
options[tup[0]] = tup[1];
});
}
displayTuples.sort((a, b) =>
a[0].localeCompare(b[0],
object.attr("bibtex_sort_language"),
options));
for (var i = 0; i < displayTuples.length; i++) {
var key = displayTuples[i][1];
var text = displayTuples[i][2];
object.append($("").attr("value", key).text(text));
}
}
var defaultTemplate = "