0c24beee3f
fixes date and et al. handling bugs in citation engine permits citation of multiple items in Word integration
1508 lines
No EOL
41 KiB
JavaScript
1508 lines
No EOL
41 KiB
JavaScript
/*
|
|
* Scholar.Cite: a class for creating bibliographies from within Scholar
|
|
* this class handles pulling the CSL file and item data out of the database,
|
|
* while CSL, below, handles the actual generation of the bibliography
|
|
*/
|
|
default xml namespace = "http://purl.org/net/xbiblio/csl";
|
|
|
|
Scholar.Cite = new function() {
|
|
var _lastCSL = null;
|
|
var _lastStyle = null;
|
|
|
|
this.getBibliography = getBibliography;
|
|
this.getCitation = getCitation;
|
|
this.getStyles = getStyles;
|
|
|
|
function getStyles() {
|
|
// get styles
|
|
var sql = "SELECT cslID, title FROM csl ORDER BY title";
|
|
var styles = Scholar.DB.query(sql);
|
|
|
|
// convert to associative array
|
|
var stylesObject = new Object();
|
|
for each(var style in styles) {
|
|
stylesObject[style.cslID] = style.title;
|
|
}
|
|
|
|
return stylesObject;
|
|
}
|
|
|
|
/*
|
|
* generates a bibliography
|
|
*/
|
|
function getBibliography(cslID, items, format) {
|
|
var csl = _getCSL(cslID);
|
|
|
|
// set items
|
|
csl.setItems(items);
|
|
|
|
// generate bibliography
|
|
return csl.createBibliography(format);
|
|
}
|
|
|
|
/*
|
|
* generates a bibliography
|
|
*/
|
|
function getCitation(cslID, item, items, format) {
|
|
var csl = _getCSL(cslID);
|
|
|
|
// set items
|
|
csl.setItems(items);
|
|
|
|
// generate bibliography
|
|
return csl.createCitation(item, format);
|
|
}
|
|
|
|
/*
|
|
* gets CSL from the database, or, if it's the most recently used style,
|
|
* from the cache
|
|
*/
|
|
function _getCSL(cslID) {
|
|
if(_lastStyle != cslID) {
|
|
// get style
|
|
var sql = "SELECT csl FROM csl WHERE cslID = ?";
|
|
var style = Scholar.DB.valueQuery(sql, [cslID]);
|
|
|
|
// create a CSL instance
|
|
_lastCSL = new CSL(style);
|
|
_lastStyle = cslID;
|
|
}
|
|
return _lastCSL;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* CSL: a class for creating bibliographies from CSL files
|
|
* this is abstracted as a separate class for the benefit of anyone who doesn't
|
|
* want to use the Scholar data model, but does want to use CSL in JavaScript
|
|
*/
|
|
CSL = function(csl) {
|
|
this._csl = new XML(this._cleanXML(csl));
|
|
|
|
// initialize CSL
|
|
this._init();
|
|
|
|
// load localizations
|
|
this._terms = this._parseLocales(this._csl.terms);
|
|
|
|
// load class defaults
|
|
this._class = this._csl["@class"].toString();
|
|
Scholar.debug("CSL: style class is "+this._class);
|
|
|
|
this._defaults = new Object();
|
|
// load class defaults
|
|
if(CSL._classDefaults[this._class]) {
|
|
var classDefaults = CSL._classDefaults[this._class];
|
|
for(var i in classDefaults) {
|
|
this._defaults[i] = classDefaults[i];
|
|
}
|
|
}
|
|
// load defaults from CSL
|
|
this._parseFieldDefaults(this._csl.defaults);
|
|
// parse bibliography options, since these will be necessary for
|
|
// disambiguation purposes even for citations
|
|
this._parseBibliographyOptions();
|
|
}
|
|
|
|
/*
|
|
* set items - convert to array and pre-process
|
|
*/
|
|
CSL.prototype.setItems = function(items) {
|
|
var serializedItemString = "";
|
|
for each(var item in items) {
|
|
serializedItemString += item.getID()+",";
|
|
}
|
|
|
|
Scholar.debug("CSL: items set to "+serializedItemString);
|
|
|
|
if(serializedItemString != this._serializedItemString) {
|
|
// only re-process if there are new items
|
|
this._serializedItemString = serializedItemString;
|
|
this._items = items;
|
|
this._preprocessItems();
|
|
}
|
|
}
|
|
|
|
/*
|
|
* create a citation (in-text or footnote)
|
|
*/
|
|
CSL.prototype.createCitation = function(items, format) {
|
|
Scholar.debug("CSL: creating citation for item "+items[0].getID());
|
|
if(!this._cit) {
|
|
this._parseCitationOptions();
|
|
}
|
|
|
|
var string = "";
|
|
for(var i in items) {
|
|
if(this._cit.format && this._cit.format.delimiter && string) {
|
|
// add delimiter if one exists, and this isn't the first element
|
|
// with content
|
|
string += this._cit.format.delimiter;
|
|
}
|
|
string += this._getCitation(items[i], format, this._cit);
|
|
}
|
|
|
|
// add format
|
|
if(this._cit.format) {
|
|
// add citation prefix or suffix
|
|
if(this._cit.format.prefix) {
|
|
string = this._cit.format.prefix + string ;
|
|
}
|
|
if(this._cit.format.suffix) {
|
|
string += this._cit.format.suffix;
|
|
}
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
/*
|
|
* create a bibliography
|
|
* (items is expected to be an array of items)
|
|
*/
|
|
CSL.prototype.createBibliography = function(format) {
|
|
if(!this._bib) {
|
|
// decide whether to parse bibliography, or, if none exists, citation
|
|
Scholar.debug("CSL: using citation element");
|
|
if(!this._cit) {
|
|
this._parseCitationOptions();
|
|
}
|
|
this._bib = this._cit;
|
|
}
|
|
|
|
// preprocess this._items
|
|
Scholar.debug("CSL: preprocessing items");
|
|
this._preprocessItems();
|
|
|
|
// process this._items
|
|
var output = "";
|
|
|
|
if(format == "HTML") {
|
|
if(this._class == "note") {
|
|
output += '<ol>\r\n';
|
|
} else if(this._bib.hangingIndent) {
|
|
output += '<div style="margin-left:0.5in;text-indent:-0.5in;">\r\n';
|
|
}
|
|
} else if(format == "RTF") {
|
|
var index = 0;
|
|
output += "{\\rtf\\ansi{\\fonttbl\\f0\\froman Times New Roman;}{\\colortbl;\\red255\\green255\\blue255;}\\pard\\f0";
|
|
if(this._bib.hangingIndent) {
|
|
output += "\\li720\\fi-720";
|
|
}
|
|
output += "\r\n";
|
|
}
|
|
|
|
for(var i in this._items) {
|
|
var item = this._items[i];
|
|
|
|
var string = this._getCitation(item, format, this._bib);
|
|
|
|
// add line feeds
|
|
if(format == "HTML") {
|
|
var coins = Scholar.OpenURL.createContextObject(item, "1.0");
|
|
if(coins) {
|
|
string += '<span class="Z3988" title="'+coins.replace("&", "&")+'"></span>';
|
|
}
|
|
|
|
if(this._class == "note") {
|
|
output += "<li>"+string+"</li>\r\n";
|
|
} else {
|
|
output += "<p>"+string+"</p>\r\n";
|
|
}
|
|
} else if(format == "RTF") {
|
|
if(this._class == "note") {
|
|
index++;
|
|
output += index+". ";
|
|
}
|
|
output += string+"\\\r\n\\\r\n";
|
|
} else if(format == "Integration") {
|
|
output += string+"\r\n\r\n";
|
|
}
|
|
}
|
|
|
|
if(format == "HTML") {
|
|
if(this._class == "note") {
|
|
output += '</ol>';
|
|
} else if(this._bib.hangingIndent) {
|
|
output += '</div>';
|
|
}
|
|
} else if(format == "RTF") {
|
|
// drop last 6 characters of output (last two returns)
|
|
output = output.substr(0, output.length-6)+"}";
|
|
}
|
|
|
|
return output;
|
|
}
|
|
|
|
|
|
CSL._months = ["January", "February", "March", "April", "May", "June", "July",
|
|
"August", "September", "October", "November", "December"];
|
|
CSL._monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
|
|
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
|
|
// for elements that inherit defaults from each other
|
|
CSL._inherit = {
|
|
author:"contributor",
|
|
editor:"contributor",
|
|
translator:"contributor",
|
|
pages:"locator",
|
|
volume:"locator",
|
|
issue:"locator",
|
|
isbn:"identifier",
|
|
doi:"identifier",
|
|
edition:"version"
|
|
}
|
|
// for class definitions
|
|
CSL._classDefaults = new Object();
|
|
CSL._classDefaults["author-date"] = {
|
|
author:{
|
|
substitute:[
|
|
{name:"editor"},
|
|
{name:"translator"},
|
|
{name:"titles", relation:"container", "font-style":"italic"},
|
|
{name:"titles", children:[
|
|
{name:"title", form:"short"}
|
|
]}
|
|
]
|
|
}
|
|
};
|
|
|
|
CSL.ns = "http://purl.org/net/xbiblio/csl";
|
|
|
|
CSL.prototype._cleanXML = function(xml) {
|
|
return xml.replace(/<\?[^>]*\?>/g, "");
|
|
}
|
|
|
|
CSL.prototype._init = function() {
|
|
if(!CSL._xmlLang) {
|
|
// get XML lang
|
|
var localeService = Components.classes['@mozilla.org/intl/nslocaleservice;1'].
|
|
getService(Components.interfaces.nsILocaleService);
|
|
CSL._xmlLang = localeService.getLocaleComponentForUserAgent();
|
|
|
|
// read locales.xml from directory
|
|
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"].
|
|
createInstance();
|
|
req.open("GET", "chrome://scholar/locale/locales.xml", false);
|
|
req.overrideMimeType("text/plain");
|
|
req.send(null);
|
|
|
|
// get default terms
|
|
var locales = new XML(this._cleanXML(req.responseText));
|
|
CSL._defaultTerms = this._parseLocales(locales);
|
|
}
|
|
}
|
|
|
|
CSL.prototype._parseLocales = function(termXML) {
|
|
// return defaults if there are no terms
|
|
if(!termXML.length()) {
|
|
return (CSL._defaultTerms ? CSL._defaultTerms : {});
|
|
}
|
|
|
|
var xml = new Namespace("http://www.w3.org/XML/1998/namespace");
|
|
|
|
// get proper locale
|
|
var locale = termXML.locale.(@xml::lang == CSL._xmlLang);
|
|
if(!locale.length()) {
|
|
var xmlLang = CSL._xmlLang.substr(0, 2);
|
|
locale = termXML.locale.(@xml::lang == xmlLang);
|
|
}
|
|
if(!locale.length()) {
|
|
// return defaults if there are no locales
|
|
return (CSL._defaultTerms ? CSL._defaultTerms : {});
|
|
}
|
|
|
|
var termArray = new Object();
|
|
termArray["default"] = new Object();
|
|
|
|
if(CSL._defaultTerms) {
|
|
// ugh. copy default array. javascript dumb.
|
|
for(var i in CSL._defaultTerms) {
|
|
termArray[i] = new Object();
|
|
for(var j in CSL._defaulTerms[i]) {
|
|
if(typeof(CSL._defaultTerms[i]) == "object") {
|
|
termArray[i][j] = [CSL._defaultTerms[i][j][0],
|
|
CSL._defaultTerms[i][j][1]];
|
|
} else {
|
|
termArray[i][j] = CSL._defaultTerms[i][j];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// loop through terms
|
|
this._parseTerms(locale.term, termArray["default"]);
|
|
// loop through term sets
|
|
locale._termSets = new Object();
|
|
for each(var termSet in locale["term-set"]) {
|
|
var name = termSet.@name.toString();
|
|
if(!termArray[name]) {
|
|
termArray[name] = new Object();
|
|
}
|
|
|
|
this._parseTerms(termSet.term, termArray[name]);
|
|
}
|
|
|
|
return termArray;
|
|
}
|
|
|
|
CSL.prototype._parseTerms = function(terms, termArray) {
|
|
for each(var term in terms) {
|
|
var name = term.@name.toString();
|
|
if(!name) {
|
|
throw("citations cannot be generated: no name defined on term in CSL");
|
|
}
|
|
|
|
var single = term.single.text().toString();
|
|
var multiple = term.multiple.text().toString();
|
|
if(single || multiple) {
|
|
if((single && multiple) // if there's both elements or
|
|
|| !termArray[name]) { // no previously defined value
|
|
termArray[name] = [single, multiple];
|
|
} else {
|
|
if(typeof(termArray[name]) != "object") {
|
|
termArray[name] = [termArray[name], termArray[name]];
|
|
}
|
|
|
|
// redefine either single or multiple
|
|
if(single) {
|
|
termArray[name][0] = single;
|
|
} else {
|
|
termArray[name][1] = multiple;
|
|
}
|
|
}
|
|
} else {
|
|
termArray[name] = term.text().toString();
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* parses attributes and children for a CSL field
|
|
*/
|
|
CSL.prototype._parseFieldAttrChildren = function(element, desc, ignoreChildren) {
|
|
if(!desc) {
|
|
var desc = new Object();
|
|
}
|
|
|
|
// copy attributes
|
|
var attributes = element.attributes();
|
|
for each(var attribute in attributes) {
|
|
desc[attribute.name()] = attribute.toString();
|
|
}
|
|
|
|
var children = element.children();
|
|
if(!ignoreChildren) {
|
|
if(children.length()) {
|
|
// parse children
|
|
|
|
if(children.length() > element.substitute.length()) {
|
|
// if there are non-substitute children, clear the current children
|
|
// array
|
|
desc.children = new Array();
|
|
}
|
|
|
|
// add children to children array
|
|
for each(var child in children) {
|
|
if(child.namespace() == CSL.ns) { // ignore elements in other
|
|
// namespaces
|
|
// parse recursively
|
|
var name = child.localName();
|
|
if(name == "substitute") {
|
|
// place substitutes in their own key, so that they're
|
|
// overridden separately
|
|
if(child.choose.length) { // choose
|
|
desc.substitute = new Array();
|
|
|
|
var chooseChildren = child.choose.children();
|
|
for each(var choose in chooseChildren) {
|
|
if(choose.namespace() == CSL.ns) {
|
|
var option = new Object();
|
|
option.name = choose.localName();
|
|
this._parseFieldAttrChildren(choose, option);
|
|
desc.substitute.push(option);
|
|
}
|
|
}
|
|
} else { // don't choose
|
|
desc.substitute = child.text().toString();
|
|
}
|
|
} else {
|
|
var childDesc = this._parseFieldAttrChildren(child);
|
|
childDesc.name = name;
|
|
desc.children.push(childDesc);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
return desc;
|
|
}
|
|
|
|
/*
|
|
* parses a list of fields into a defaults associative array
|
|
*/
|
|
CSL.prototype._parseFieldDefaults = function(ref) {
|
|
for each(var element in ref.children()) {
|
|
if(element.namespace() == CSL.ns) { // ignore elements in other namespaces
|
|
var name = element.localName();
|
|
Scholar.debug("CSL: parsing field defaults for "+name);
|
|
var fieldDesc = this._parseFieldAttrChildren(element);
|
|
|
|
if(this._defaults[name]) { // inherit from existing defaults
|
|
this._defaults[name] = this._merge(this._defaults[name],
|
|
fieldDesc);
|
|
} else {
|
|
this._defaults[name] = fieldDesc;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* parses a list of fields into an array of objects
|
|
*/
|
|
CSL.prototype._parseFields = function(ref, type, bibCitElement, inheritFormat) {
|
|
var typeDesc = new Array();
|
|
for each(var element in ref) {
|
|
if(element.namespace() == CSL.ns) { // ignore elements in other namespaces
|
|
var itemDesc = new Object();
|
|
itemDesc.name = element.localName();
|
|
|
|
// add defaults, but only if we're parsing as a reference type
|
|
if(type != undefined) {
|
|
var fieldDefaults = this._getFieldDefaults(itemDesc.name);
|
|
itemDesc = this._merge(fieldDefaults, itemDesc);
|
|
if(bibCitElement && inheritFormat) {
|
|
itemDesc = this._merge(bibCitElement.inheritFormat, itemDesc);
|
|
}
|
|
|
|
// create serialized representation
|
|
itemDesc._serialized = this._serializeElement(itemDesc.name, itemDesc);
|
|
// add to serialization for type
|
|
if(bibCitElement) {
|
|
bibCitElement._serializations[type][itemDesc._serialized] = itemDesc;
|
|
}
|
|
}
|
|
|
|
// parse group children
|
|
if(itemDesc.name == "group") {
|
|
// parse attributes on this field, but ignore children
|
|
this._parseFieldAttrChildren(element, itemDesc, true);
|
|
|
|
var children = element.children();
|
|
if(children.length()) {
|
|
itemDesc.children = this._parseFields(children, type, bibCitElement);
|
|
}
|
|
} else {
|
|
// parse attributes on this field
|
|
this._parseFieldAttrChildren(element, itemDesc);
|
|
}
|
|
|
|
typeDesc.push(itemDesc);
|
|
}
|
|
}
|
|
return typeDesc;
|
|
}
|
|
|
|
/*
|
|
* parses an et al field
|
|
*/
|
|
CSL.prototype._parseEtAl = function(etAl, bibCitElement) {
|
|
if(etAl.length()) {
|
|
bibCitElement.etAl = new Object();
|
|
|
|
if(etAl.length() > 1) {
|
|
// separate first and subsequent et als
|
|
for each(var etAlElement in etAl) {
|
|
if(etAlElement.@position == "subsequent") {
|
|
bibCitElement.subsequentEtAl = new Object();
|
|
bibCitElement.subsequentEtAl.minCreators = parseInt(etAlElement['@min-authors']);
|
|
bibCitElement.subsequentEtAl.useFirst = parseInt(etAlElement['@use-first']);
|
|
} else {
|
|
var parseElement = etAlElement;
|
|
}
|
|
}
|
|
} else {
|
|
var parseElement = etAl;
|
|
}
|
|
|
|
bibCitElement.etAl.minCreators = parseInt(parseElement['@min-authors']);
|
|
bibCitElement.etAl.useFirst = parseInt(parseElement['@use-first']);
|
|
}
|
|
}
|
|
|
|
/*
|
|
* parses cs-format attributes into just a prefix and a suffix; accepts an
|
|
* optional array of cs-format
|
|
*/
|
|
CSL.prototype._parseBibliographyOptions = function() {
|
|
var bibliography = this._csl.bibliography;
|
|
this._bib = new Object();
|
|
|
|
// global prefix and suffix format information
|
|
this._bib.inheritFormat = new Array();
|
|
for each(var attribute in bibliography.layout.item.attributes()) {
|
|
this._bib.inheritFormat[attribute.name()] = attribute.toString();
|
|
}
|
|
|
|
// sections (TODO)
|
|
this._bib.sections = [{groupBy:"default",
|
|
heading:bibliography.layout.heading.text["@term-name"].toString()}];
|
|
for each(var section in bibliography.layout.section) {
|
|
this._bib.sections.push([{groupBy:section["@group-by"].toString(),
|
|
heading:section.heading.text["@term-name"].toString()}]);
|
|
}
|
|
|
|
// subsequent author substitute
|
|
// replaces subsequent occurances of an author with a given string
|
|
if(bibliography['@subsequent-author-substitute']) {
|
|
this._bib.subsequentAuthorSubstitute = bibliography['@subsequent-author-substitute'].toString();
|
|
}
|
|
|
|
// hanging indent
|
|
if(bibliography['@hanging-indent']) {
|
|
this._bib.hangingIndent = true;
|
|
}
|
|
|
|
// sort order
|
|
var algorithm = bibliography.sort.@algorithm.toString();
|
|
if(algorithm) {
|
|
// for classes, use the sort order that
|
|
if(algorithm == "author-date") {
|
|
this._bib.sortOrder = [this._getFieldDefaults("author"),
|
|
this._getFieldDefaults("date")];
|
|
this._bib.sortOrder[0].name = "author";
|
|
this._bib.sortOrder[1].name = "date";
|
|
} else if(algorithm == "label") {
|
|
this._bib.sortOrder = [this._getFieldDefaults("label")];
|
|
this._bib.sortOrder[0].name = "label";
|
|
} else if(algorithm == "cited") {
|
|
this._bib.sortOrder = [this._getFieldDefaults("cited")];
|
|
this._bib.sortOrder[0].name = "cited";
|
|
}
|
|
} else {
|
|
this._bib.sortOrder = this._parseFields(bibliography.sort, false, this._bib);
|
|
}
|
|
|
|
// parse et al
|
|
this._parseEtAl(bibliography["et-al"], this._bib);
|
|
|
|
// parse types
|
|
this._parseTypes(this._csl.bibliography.layout.item, this._bib);
|
|
}
|
|
|
|
/*
|
|
* parses cs-format attributes into just a prefix and a suffix; accepts an
|
|
* optional array of cs-format
|
|
*/
|
|
CSL.prototype._parseCitationOptions = function() {
|
|
var citation = this._csl.citation;
|
|
this._cit = new Object();
|
|
|
|
// parse et al
|
|
this._parseEtAl(citation["et-al"], this._cit);
|
|
|
|
// global format information
|
|
this._cit.format = new Array();
|
|
for each(var attribute in citation.attributes()) {
|
|
this._cit.format[attribute.name()] = attribute.toString();
|
|
}
|
|
|
|
// parse types
|
|
this._parseTypes(this._csl.citation.layout.item, this._cit);
|
|
}
|
|
|
|
/*
|
|
* determine available reference types and add their XML objects to the tree
|
|
* (they will be parsed on the fly when necessary; see _parseReferenceType)
|
|
*/
|
|
CSL.prototype._parseTypes = function(itemElements, bibCitElement) {
|
|
Scholar.debug("CSL: parsing item elements");
|
|
|
|
bibCitElement._types = new Object();
|
|
bibCitElement._serializations = new Object();
|
|
|
|
// find the type item without position="subsequent"
|
|
for each(var itemElement in itemElements) {
|
|
if(itemElement.@position.toString() == "subsequent") {
|
|
// bind subsequent element to position 1
|
|
bibCitElement._types[1] = itemElement;
|
|
bibCitElement._serializations[1] = new Object();
|
|
} else {
|
|
// create an associative array of available types
|
|
if(itemElement.choose.length()) {
|
|
for each(var type in itemElement.choose.type) {
|
|
bibCitElement._types[type.@name] = type;
|
|
bibCitElement._serializations[type.@name] = new Object();
|
|
}
|
|
} else {
|
|
// if there's only one type, bind it to index 0
|
|
bibCitElement._types[0] = itemElement;
|
|
bibCitElement._serializations[0] = new Object();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* convert reference types to native structures for speed
|
|
*/
|
|
CSL.prototype._getTypeObject = function(reftype, bibCitElement) {
|
|
if(!bibCitElement._types[reftype]) {
|
|
// no type available
|
|
return false;
|
|
}
|
|
|
|
// parse type if necessary
|
|
if(typeof(bibCitElement._types[reftype]) == "xml") {
|
|
Scholar.debug("CSL: parsing XML for "+reftype);
|
|
bibCitElement._types[reftype] = this._parseFields(bibCitElement._types[reftype].children(),
|
|
reftype, bibCitElement, true);
|
|
}
|
|
|
|
Scholar.debug("CSL: got object for "+reftype);
|
|
return bibCitElement._types[reftype];
|
|
}
|
|
|
|
/*
|
|
* merges two elements, letting the second override the first
|
|
*/
|
|
CSL.prototype._merge = function(element1, element2) {
|
|
var mergedElement = new Object();
|
|
for(var i in element1) {
|
|
mergedElement[i] = element1[i];
|
|
}
|
|
for(var i in element2) {
|
|
mergedElement[i] = element2[i];
|
|
}
|
|
return mergedElement;
|
|
}
|
|
|
|
/*
|
|
* gets defaults for a specific element; handles various inheritance rules
|
|
* (contributor, locator)
|
|
*/
|
|
CSL.prototype._getFieldDefaults = function(elementName) {
|
|
// first, see if there are specific defaults
|
|
if(this._defaults[elementName]) {
|
|
if(CSL._inherit[elementName]) {
|
|
var inheritedDefaults = this._getFieldDefaults(CSL._inherit[elementName]);
|
|
for(var i in inheritedDefaults) { // will only be called if there
|
|
// is merging necessary
|
|
return this._merge(inheritedDefaults, this._defaults[elementName]);
|
|
}
|
|
}
|
|
return this._defaults[elementName];
|
|
}
|
|
// next, try to get defaults from the item from which this item inherits
|
|
if(CSL._inherit[elementName]) {
|
|
return this._getFieldDefaults(CSL._inherit[elementName]);
|
|
}
|
|
// finally, return an empty object
|
|
return {};
|
|
}
|
|
|
|
/*
|
|
* gets a term, in singular or plural form
|
|
*/
|
|
CSL.prototype._getTerm = function(term, plural, termSet) {
|
|
if(!termSet) {
|
|
termSet = "default";
|
|
}
|
|
if(!this._terms[termSet][term]) {
|
|
return "";
|
|
}
|
|
|
|
if(typeof(this._terms[termSet][term]) == "object") { // singular and plural forms
|
|
// are available
|
|
if(plural) {
|
|
return this._terms[termSet][term][1];
|
|
} else {
|
|
return this._terms[termSet][term][0];
|
|
}
|
|
}
|
|
|
|
return this._terms[termSet][term];
|
|
}
|
|
|
|
/*
|
|
* process the date "string" into a useful object
|
|
*/
|
|
CSL.prototype._processDate = function(string) {
|
|
var date = new Object();
|
|
|
|
var dateRe = /^([0-9]{4})-([0-9]{2})-([0-9]{2})$/;
|
|
var m = dateRe.exec(string);
|
|
if(m) { // sql date
|
|
var jsDate = new Date(m[1], m[2]-1, m[3], false, false, false);
|
|
} else { // not an sql date
|
|
var yearRe = /^[0-9]+$/;
|
|
if(yearRe.test(string)) { // is a year
|
|
date.year = string;
|
|
return date;
|
|
} else { // who knows what this is
|
|
var jsDate = new Date(string)
|
|
}
|
|
}
|
|
|
|
if(isNaN(jsDate.valueOf())) { // couldn't parse
|
|
// get year and say other parts are month
|
|
var yearRe = /^(.*)([0-9]{4})(.*)$/
|
|
var m = yearRe.exec(string);
|
|
|
|
if(m) {
|
|
date.year = m[2];
|
|
date.month = m[1];
|
|
if(m[2] && m[3]) date.month += " ";
|
|
date.month += m[3];
|
|
}
|
|
} else {
|
|
date.year = jsDate.getFullYear();
|
|
date.month = jsDate.getMonth();
|
|
date.day = jsDate.getDay();
|
|
}
|
|
|
|
return date;
|
|
}
|
|
|
|
/*
|
|
* escapes a string for a given format
|
|
*/
|
|
CSL.prototype._escapeString = function(string, format) {
|
|
if(format == "HTML") {
|
|
// replace HTML entities
|
|
string = string.replace(/&/g, "&");
|
|
string = string.replace(/</g, "<");
|
|
string = string.replace(/>/g, ">");
|
|
|
|
return string;
|
|
} else if(format == "RTF") {
|
|
var newString = "";
|
|
|
|
// go through and fix up unicode entities
|
|
for(i=0; i<string.length; i++) {
|
|
var charCode = string.charCodeAt(i);
|
|
if(charCode > 127) { // encode unicode
|
|
newString += "\\uc0\\u"+charCode.toString()+" ";
|
|
} else if(charCode == 92) { // double backslashes
|
|
newString += "\\\\";
|
|
} else {
|
|
newString += string[i];
|
|
}
|
|
}
|
|
|
|
return newString;
|
|
} else if(format == "Integration") {
|
|
return string.replace(/\\/g, "\\\\");
|
|
} else {
|
|
return string;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* formats a string according to the cs-format attributes on element
|
|
*/
|
|
CSL.prototype._formatString = function(element, string, format, dontEscape) {
|
|
if(!string) return "";
|
|
if(typeof(string) != "string") {
|
|
string = string.toString();
|
|
}
|
|
|
|
// handle text transformation
|
|
if(element["text-transform"]) {
|
|
if(element["text-transform"] == "lowercase") {
|
|
// all lowercase
|
|
string = string.toLowerCase();
|
|
} else if(element["text-transform"] == "uppercase") {
|
|
// all uppercase
|
|
string = string.toUpperCase();
|
|
} else if(element["text-transform"] == "capitalize") {
|
|
// capitalize first
|
|
string = string[0].toUpperCase()+string.substr(1);
|
|
}
|
|
}
|
|
|
|
// special rule: if a field ends in a punctuation mark, and the suffix
|
|
// begins with a period, chop the period off the suffix
|
|
var suffix;
|
|
if(element.suffix) {
|
|
suffix = element.suffix; // copy so as to leave original intact
|
|
|
|
if(suffix[0] == ".") {
|
|
var lastChar = string[string.length-1];
|
|
if(lastChar == "." || lastChar == "?" || lastChar == "!") {
|
|
suffix = suffix.substr(1);
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!dontEscape) {
|
|
string = this._escapeString(string, format);
|
|
}
|
|
|
|
if(format == "HTML") {
|
|
var style = "";
|
|
|
|
var cssAttributes = ["font-family", "font-style", "font-variant",
|
|
"font-weight"];
|
|
for(var j in cssAttributes) {
|
|
if(element[cssAttributes[j]] && element[cssAttributes[j]].indexOf('"') == -1) {
|
|
style += cssAttributes[j]+":"+element[cssAttributes[j]];
|
|
}
|
|
}
|
|
|
|
if(style) {
|
|
string = '<span style="'+style+'">'+string+'</span>';
|
|
}
|
|
} else if(format == "RTF" || format == "Integration") {
|
|
if(element["font-style"] && (element["font-style"] == "oblique" || element["font-style"] == "italic")) {
|
|
string = "\\i "+string+"\\i0 ";
|
|
}
|
|
if(element["font-variant"] && element["font-variant"] == "small-caps") {
|
|
string = "\\scaps "+string+"\\scaps0 ";
|
|
}
|
|
if(element["font-weight"] && element["font-weight"] == "bold") {
|
|
string = "\\b "+string+"\\b0 ";
|
|
}
|
|
}
|
|
|
|
if(format != "compare" && element.prefix) {
|
|
string = this._escapeString(element.prefix, format)+string;
|
|
}
|
|
if(format != "compare" && suffix) {
|
|
string += this._escapeString(suffix, format);
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
/*
|
|
* formats a locator (pages, volume, issue) or an identifier (isbn, doi)
|
|
* note that label should be null for an identifier
|
|
*/
|
|
CSL.prototype._formatLocator = function(identifier, element, number, format) {
|
|
var data = "";
|
|
|
|
if(number) {
|
|
for(var i in element.children) {
|
|
var child = element.children[i];
|
|
var string = "";
|
|
|
|
if(child.name == "number") {
|
|
string = number;
|
|
} else if(child.name == "text") {
|
|
var plural = (identifier && (number.indexOf(",") != -1
|
|
|| number.indexOf("-") != -1));
|
|
string = this._getTerm(child["term-name"], plural);
|
|
} else if(identifier && child.name == "label") {
|
|
var plural = (number.indexOf(",") != -1 || number.indexOf("-") != -1);
|
|
string = this._getTerm(identifier, plural, child["term-set"]);
|
|
}
|
|
|
|
if(string) {
|
|
data += this._formatString(child, string, format);
|
|
}
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
* format the date in format supplied by element from the date object
|
|
* returned by this._processDate
|
|
*/
|
|
CSL.prototype._formatDate = function(element, date, format) {
|
|
if(format == "disambiguate") {
|
|
// for disambiguation, return only the year
|
|
return date.year;
|
|
}
|
|
|
|
var data = "";
|
|
|
|
for(var i in element.children) {
|
|
var child = element.children[i];
|
|
var string = "";
|
|
|
|
if(child.name == "year" && date.year) {
|
|
if(format == "compare") {
|
|
string = this._lpad(date.year, "0", 4);
|
|
} else {
|
|
string = date.year.toString();
|
|
if(date.disambiguation) {
|
|
string += date.disambiguation;
|
|
}
|
|
}
|
|
} else if(child.name == "month" && date.month) {
|
|
if(format == "compare") {
|
|
string = this._lpad(date.month+1, "0", 2);
|
|
} else {
|
|
if(element.form == "short") {
|
|
string = CSL._monthsShort[date.month];
|
|
} else {
|
|
string = CSL._months[date.month];
|
|
}
|
|
}
|
|
} else if(child.name == "day" && date.day) {
|
|
if(format == "compare") {
|
|
string = this._lpad(date.day, "0", 2);
|
|
} else {
|
|
string = date.day.toString();
|
|
}
|
|
}
|
|
|
|
if(string) {
|
|
data += this._formatString(child, string, format);
|
|
}
|
|
}
|
|
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
* serializes an element into a string suitable to prevent substitutes from
|
|
* recurring in the same style
|
|
*/
|
|
CSL.prototype._serializeElement = function(name, element) {
|
|
var string = name;
|
|
if(element.relation) {
|
|
string += " relation:"+element.relation;
|
|
}
|
|
if(element.role) {
|
|
string += " role"+element.role;
|
|
}
|
|
return string;
|
|
}
|
|
|
|
/*
|
|
* pads a number or other string with a given string on the left
|
|
*/
|
|
CSL.prototype._lpad = function(string, pad, length) {
|
|
while(string.length < length) {
|
|
string = pad + string;
|
|
}
|
|
return string;
|
|
}
|
|
|
|
/*
|
|
* preprocess items, separating authors, editors, and translators arrays into
|
|
* separate properties
|
|
*/
|
|
CSL.prototype._preprocessItems = function() {
|
|
// get data necessary to generate citations before sorting
|
|
for(var i in this._items) {
|
|
var item = this._items[i];
|
|
|
|
if(!item._csl) {
|
|
// namespace everything in item._csl so there's no chance of overlap
|
|
item._csl = new Object();
|
|
item._csl.ignore = new Array();
|
|
|
|
// separate item into authors, editors, translators
|
|
var creators = this._separateItemCreators(item);
|
|
item._csl.authors = creators[0];
|
|
item._csl.editors = creators[1];
|
|
item._csl.translators = creators[2];
|
|
|
|
// parse date
|
|
item._csl.date = CSL.prototype._processDate(item.getField("date"));
|
|
} else {
|
|
// clear disambiguation and subsequent author substitute
|
|
if(item._csl.disambiguation) item._csl.disambiguation = undefined;
|
|
if(item._csl.subsequentAuthorSubstitute) item._csl.subsequentAuthorSubstitute = undefined;
|
|
}
|
|
}
|
|
|
|
// sort by sort order
|
|
if(this._bib.sortOrder) {
|
|
Scholar.debug("CSL: sorting this._items");
|
|
var me = this;
|
|
this._items.sort(function(a, b) {
|
|
return me._compareItem(a, b);
|
|
});
|
|
}
|
|
|
|
// disambiguate items after preprocessing and sorting
|
|
var usedCitations = new Array();
|
|
var lastAuthor;
|
|
|
|
for(var i in this._items) {
|
|
var item = this._items[i];
|
|
|
|
var author = this._getFieldValue("author",
|
|
this._getFieldDefaults("author"),
|
|
item, "disambiguate", this._bib);
|
|
|
|
// handle (2006a) disambiguation for author-date styles
|
|
if(this._class == "author-date") {
|
|
var citation = author+" "+this._getFieldValue("date",
|
|
this._getFieldDefaults("date"),
|
|
item, "disambiguate", this._bib);
|
|
|
|
if(usedCitations[citation]) {
|
|
if(!usedCitations[citation]._csl.date.disambiguation) {
|
|
usedCitations[citation]._csl.date.disambiguation = "a";
|
|
item._csl.date.disambiguation = "b";
|
|
} else {
|
|
// get all but last character
|
|
var oldLetter = usedCitations[citation]._csl.date.disambiguation;
|
|
if(oldLetter.length > 1) {
|
|
item._csl.date.disambiguation = oldLetter.substr(0, oldLetter.length-1);
|
|
} else {
|
|
item._csl.date.disambiguation = "";
|
|
}
|
|
|
|
var charCode = oldLetter.charCodeAt(oldLetter.length-1);
|
|
if(charCode == 122) {
|
|
// item is z; add another letter
|
|
item._csl.date.disambiguation += "za";
|
|
} else {
|
|
// next lowercase letter
|
|
item._csl.date.disambiguation += String.fromCharCode(charCode+1);
|
|
}
|
|
}
|
|
}
|
|
|
|
usedCitations[citation] = item;
|
|
}
|
|
|
|
// add numbers to each
|
|
item._csl.number = i;
|
|
|
|
// handle subsequent author substitutes
|
|
if(this._bib.subsequentAuthorSubstitute && lastAuthor == author) {
|
|
item._csl.subsequentAuthorSubstitute = true;
|
|
}
|
|
lastAuthor = author;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* handles sorting of items
|
|
*/
|
|
CSL.prototype._compareItem = function(a, b, opt) {
|
|
for(var i in this._bib.sortOrder) {
|
|
var sortElement = this._bib.sortOrder[i];
|
|
|
|
var aValue = this._getFieldValue(sortElement.name, sortElement, a,
|
|
"compare", this._bib);
|
|
var bValue = this._getFieldValue(sortElement.name, sortElement, b,
|
|
"compare", this._bib);
|
|
if(bValue > aValue) {
|
|
return -1;
|
|
} else if(bValue < aValue) {
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
// finally, give up; they're the same
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* process creator objects; if someone had a creator model that handled
|
|
* non-Western names better than ours, this would be the function to change
|
|
*/
|
|
CSL.prototype._processCreators = function(type, element, creators, format, bibCitElement) {
|
|
var maxCreators = creators.length;
|
|
if(!maxCreators) return;
|
|
|
|
if(format == "disambiguate") {
|
|
// for disambiguation, return only the last name of the first creator
|
|
return creators[0].lastName;;
|
|
}
|
|
if(!element.children) {
|
|
return "";
|
|
}
|
|
|
|
var data = "";
|
|
for(var i in element.children) {
|
|
var child = element.children[i];
|
|
var string = "";
|
|
|
|
if(child.name == "name") {
|
|
var useEtAl = false;
|
|
|
|
// figure out if we need to use "et al"
|
|
if(bibCitElement.etAl && maxCreators >= bibCitElement.etAl.minCreators) {
|
|
maxCreators = bibCitElement.etAl.useFirst;
|
|
useEtAl = true;
|
|
}
|
|
|
|
// parse authors into strings
|
|
var authorStrings = [];
|
|
var firstName, lastName;
|
|
for(var i=0; i<maxCreators; i++) {
|
|
var firstName = "";
|
|
if(element["form"] != "short") {
|
|
if(child["initialize-with"] != undefined) {
|
|
// even if initialize-with is simply an empty string, use
|
|
// initials
|
|
|
|
// use first initials
|
|
var firstNames = creators[i].firstName.split(" ");
|
|
for(var j in firstNames) {
|
|
if(firstNames[j]) {
|
|
// get first initial, put in upper case, add initializeWith string
|
|
firstName += firstNames[j][0].toUpperCase()+child["initialize-with"];
|
|
}
|
|
}
|
|
} else {
|
|
firstName = creators[i].firstName;
|
|
}
|
|
}
|
|
lastName = creators[i].lastName;
|
|
|
|
if(element["name-as-sort-order"]
|
|
&& ((i == 0 && element["name-as-sort-order"] == "first")
|
|
|| element["name-as-sort-order"] == "all")
|
|
&& child["sort-separator"]) {
|
|
// if this is the first author and name-as-sort="first"
|
|
// or if this is a subsequent author and name-as-sort="all"
|
|
// then the name gets inverted
|
|
authorStrings.push(lastName+(firstName ? child["sort-separator"]+firstName : ""));
|
|
} else {
|
|
authorStrings.push((firstName ? firstName+" " : "")+lastName);
|
|
}
|
|
}
|
|
|
|
// figure out if we need an "and" or an "et al"
|
|
var joinString = ", ";
|
|
if(maxCreators > 1) {
|
|
if(useEtAl) { // multiple creators and need et al
|
|
authorStrings.push(this._getTerm("et-al"));
|
|
} else { // multiple creators but no et al
|
|
// add and to last creator
|
|
if(child["and"]) {
|
|
if(child["and"] == "symbol") {
|
|
var and = "&"
|
|
} else {
|
|
var and = this._getTerm("and");
|
|
}
|
|
|
|
authorStrings[maxCreators-1] = and+" "+authorStrings[maxCreators-1];
|
|
// skip the comma if there are only two creators and no
|
|
// et al, and name as sort is no
|
|
if(maxCreators == 2 &&
|
|
(!element["name-as-sort-order"]
|
|
|| (element["name-as-sort-order"] != "first"
|
|
&& element["name-as-sort-order"] != "all"))) {
|
|
joinString = " ";
|
|
}
|
|
}
|
|
}
|
|
}
|
|
string = authorStrings.join(joinString);
|
|
} else if(child.name == "label") {
|
|
string = this._getTerm(type, (maxCreators != 1), child["term-set"]);
|
|
}
|
|
|
|
|
|
// add string to data
|
|
if(string) {
|
|
data += this._formatString(child, string, format);
|
|
}
|
|
}
|
|
|
|
// add to the data
|
|
return data;
|
|
}
|
|
|
|
/*
|
|
* get a citation, given an item and bibCitElement
|
|
*/
|
|
CSL.prototype._getCitation = function(item, format, bibCitElement) {
|
|
Scholar.debug("CSL: generating citation for item "+item.getID());
|
|
|
|
// determine mapping
|
|
if(bibCitElement._types[0]) {
|
|
// only one element
|
|
var typeName = 0;
|
|
var type = this._getTypeObject(typeName, bibCitElement);
|
|
} else {
|
|
var typeNames = this._getTypeFromItem(item);
|
|
for each(var typeName in typeNames) {
|
|
var type = this._getTypeObject(typeName, bibCitElement);
|
|
if(type) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!type) {
|
|
throw("CSL: ERROR: no type found for item");
|
|
}
|
|
Scholar.debug("CSL: using CSL type "+typeName);
|
|
|
|
var string = "";
|
|
for(var j in type) {
|
|
var value = this._getFieldValue(type[j].name, type[j], item, format,
|
|
bibCitElement, typeName);
|
|
string += value;
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
/*
|
|
* processes an element from a (pre-processed) item into text
|
|
*/
|
|
CSL.prototype._getFieldValue = function(name, element, item, format, bibCitElement, typeName) {
|
|
var data = "";
|
|
|
|
if(element._serialized && item._csl.ignore[element._serialized]) {
|
|
return "";
|
|
}
|
|
|
|
// controls whether formatted strings need to be escaped a second time
|
|
var dontEscape = true;
|
|
|
|
if(name == "author") {
|
|
if(item._csl.subsequentAuthorSubstitute && bibCitElement.subsequentAuthorSubstitute) {
|
|
// handle subsequent author substitute behavior
|
|
data = bibCitElement.subsequentAuthorSubstitute;
|
|
} else {
|
|
data = this._processCreators(name, element, item._csl.authors, format, bibCitElement);
|
|
}
|
|
} else if(name == "editor") {
|
|
data = this._processCreators(name, element, item._csl.editors, format, bibCitElement);
|
|
} else if(name == "translator") {
|
|
data = this._processCreators(name, element, item._csl.translators, format, bibCitElement);
|
|
} else if(name == "titles") {
|
|
for(var i in element.children) {
|
|
var child = element.children[i];
|
|
var string = "";
|
|
|
|
if(child.name == "title") { // for now, we only care about the
|
|
// "title" sub-element
|
|
if(!element.relation) {
|
|
string = item.getField("title");
|
|
} else if(element.relation == "container") {
|
|
string = item.getField("publicationTitle");
|
|
} else if(element.relation == "collection") {
|
|
string = item.getField("seriesTitle");
|
|
}
|
|
}
|
|
|
|
if(string) {
|
|
data += this._formatString(child, string, format);
|
|
}
|
|
}
|
|
} else if(name == "date") {
|
|
data = this._formatDate(element, item._csl.date, format);
|
|
} else if(name == "publisher") {
|
|
for(var i in element.children) {
|
|
var child = element.children[i];
|
|
var string = "";
|
|
|
|
if(child.name == "place") {
|
|
string = item.getField("place");
|
|
} else if(child.name == "name") {
|
|
string = item.getField("publisher");
|
|
}
|
|
|
|
if(string) {
|
|
data += this._formatString(child, string, format);
|
|
}
|
|
}
|
|
} else if(name == "access") {
|
|
var save = false;
|
|
|
|
for(var i in element.children) {
|
|
var child = element.children[i];
|
|
var string = "";
|
|
|
|
if(child.name == "url") {
|
|
string = item.getField("url");
|
|
} else if(child.name == "date") {
|
|
var field = item.getField("accessDate");
|
|
if(field) {
|
|
string = this._formatDate(child, this._processDate(field), format);
|
|
}
|
|
} else if(child.name == "physicalLocation") {
|
|
string = item.getField("archiveLocation");
|
|
} else if(child.name == "text") {
|
|
string = this._getTerm(child["term-name"]);
|
|
}
|
|
|
|
if(string) {
|
|
data += this._formatString(child, string, format);
|
|
if(child.name != "text") {
|
|
// only save if there's non-text data
|
|
save = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if(!save) {
|
|
data = "";
|
|
}
|
|
} else if(name == "volume" || name == "issue") {
|
|
var field = item.getField(name);
|
|
if(field) {
|
|
data = this._formatLocator(name, element, field, format);
|
|
}
|
|
} else if(name == "pages") {
|
|
var field = item.getField("pages");
|
|
if(field) {
|
|
data = this._formatLocator("page", element, field, format);
|
|
}
|
|
} else if(name == "edition") {
|
|
data = item.getField("edition");
|
|
dontEscape = false;
|
|
} else if(name == "genre") {
|
|
data = item.getField("type");
|
|
if(!data) {
|
|
data = item.getField("thesisType");
|
|
}
|
|
dontEscape = false;
|
|
} else if(name == "group") {
|
|
var childData = new Array();
|
|
for(var i in element.children) {
|
|
// get data for each child element
|
|
var child = element.children[i];
|
|
|
|
var string = this._getFieldValue(child.name, child, item,
|
|
format, typeName, bibCitElement);
|
|
if(string) {
|
|
childData.push(string);
|
|
}
|
|
}
|
|
|
|
// implode with delimiter
|
|
data = childData.join((element["delimiter"] ? element["delimiter"] : ""));
|
|
} else if(name == "text") {
|
|
data = this._getTerm(element["term-name"]);
|
|
dontEscape = false;
|
|
} else if(name == "isbn" || name == "doi") {
|
|
var field = item.getField(name.toUpperCase());
|
|
if(field) {
|
|
data = this._formatLocator(null, element, field, format);
|
|
}
|
|
} else if(name == "number") {
|
|
data = this._csl.number;
|
|
dontEscape = false;
|
|
}
|
|
|
|
if(data) {
|
|
return this._formatString(element, data, format, dontEscape);
|
|
} else if(element.substitute) {
|
|
// try each substitute element until one returns something
|
|
for(var i in element.substitute) {
|
|
var substituteElement = element.substitute[i];
|
|
var serialization = this._serializeElement(substituteElement.name,
|
|
substituteElement);
|
|
|
|
var inheritElement;
|
|
if(CSL._inherit[substituteElement.name] && CSL._inherit[name]
|
|
&& CSL._inherit[substituteElement.name] == CSL._inherit[name]) {
|
|
// if both substituteElement and the parent element inherit from
|
|
// the same base element, apply styles here
|
|
inheritElement = element;
|
|
} else {
|
|
// search for elements with the same serialization
|
|
if(typeName != undefined && bibCitElement._serializations[typeName]
|
|
&& bibCitElement._serializations[typeName][serialization]) {
|
|
inheritElement = bibCitElement._serializations[typeName][serialization];
|
|
} else {
|
|
// otherwise, use defaults
|
|
inheritElement = this._getFieldDefaults(substituteElement.name);
|
|
}
|
|
}
|
|
|
|
// merge inheritElement and element
|
|
substituteElement = this._merge(inheritElement, substituteElement);
|
|
// regardless of inheritance pathway, make sure substitute inherits
|
|
// general prefix and suffix from the element it's substituting for
|
|
substituteElement.prefix = element.prefix;
|
|
substituteElement.suffix = element.suffix;
|
|
// clear substitute element off of the element we're substituting
|
|
substituteElement.substitute = undefined;
|
|
|
|
// ignore elements with the same serialization
|
|
item._csl.ignore[serialization] = true;
|
|
|
|
// get field value
|
|
data = this._getFieldValue(substituteElement.name,
|
|
substituteElement, item, format,
|
|
bibCitElement);
|
|
// return field value, if there is one; otherwise, keep processing
|
|
// the data
|
|
if(data) {
|
|
return data;
|
|
}
|
|
}
|
|
}
|
|
|
|
return "";
|
|
}
|
|
|
|
/*
|
|
* THE FOLLOWING CODE IS SCHOLAR-SPECIFIC
|
|
* gets a list of possible CSL types, in order of preference, for an item
|
|
*/
|
|
CSL._optionalTypeMappings = {
|
|
journalArticle:"article-journal",
|
|
magazineArticle:"article-magazine",
|
|
newspaperArticle:"article-newspaper",
|
|
thesis:"thesis",
|
|
letter:"personal communication",
|
|
manuscript:"manuscript",
|
|
interview:"interview",
|
|
film:"motion picture",
|
|
artwork:"graphic",
|
|
website:"webpage"
|
|
};
|
|
// TODO: check with Elena/APA/MLA on this
|
|
CSL._fallbackTypeMappings = {
|
|
book:"book",
|
|
bookSection:"chapter",
|
|
journalArticle:"article",
|
|
magazineArticle:"article",
|
|
newspaperArticle:"article",
|
|
thesis:"book",
|
|
letter:"article",
|
|
manuscript:"book",
|
|
interview:"book",
|
|
film:"book",
|
|
artwork:"book",
|
|
website:"article"
|
|
};
|
|
|
|
CSL.prototype._getTypeFromItem = function(item) {
|
|
var scholarType = Scholar.ItemTypes.getName(item.getType());
|
|
|
|
// get type
|
|
Scholar.debug("CSL: parsing item of Scholar type "+scholarType);
|
|
return [CSL._optionalTypeMappings[scholarType], CSL._fallbackTypeMappings[scholarType]];
|
|
}
|
|
|
|
CSL.prototype._separateItemCreators = function(item) {
|
|
var authors = new Array();
|
|
var editors = new Array();
|
|
var translators = new Array();
|
|
|
|
var authorID = Scholar.CreatorTypes.getID("author");
|
|
var editorID = Scholar.CreatorTypes.getID("editor");
|
|
var translatorID = Scholar.CreatorTypes.getID("translator");
|
|
|
|
var creators = item.getCreators();
|
|
for(var j in creators) {
|
|
var creator = creators[j];
|
|
|
|
if(creator.creatorTypeID == editorID) {
|
|
editors.push(creator);
|
|
} else if(creator.creatorTypeID == translatorID) {
|
|
translators.push(creator);
|
|
} else if(creator.creatorTypeID == authorID) {
|
|
// TODO: do we just ignore contributors?
|
|
authors.push(creator);
|
|
}
|
|
}
|
|
|
|
return [authors, editors, translators];
|
|
}
|
|
/*
|
|
* END SCHOLAR-SPECIFIC CODE
|
|
*/ |