Restructure and comment cite.js
This commit is contained in:
parent
0deb2573cc
commit
de2d1669fe
|
@ -1,442 +1,481 @@
|
|||
Zotero.Cite = function(){}
|
||||
Zotero.Cite.System = function(){};
|
||||
|
||||
Zotero.Cite.System._quotedRegexp = /^".+"$/;
|
||||
|
||||
// TODO: Clear this cache from time to time
|
||||
Zotero.Cite.System._cache = new Object();
|
||||
|
||||
Zotero.Cite.System.retrieveItem = function(item) {
|
||||
var zoteroItem, slashIndex;
|
||||
if(item instanceof Zotero.Item) {
|
||||
//if(this._cache[item.id]) return this._cache[item.id];
|
||||
zoteroItem = item;
|
||||
} else {
|
||||
var type = typeof item;
|
||||
if(type === "string" && (slashIndex = item.indexOf("/")) !== -1) {
|
||||
// is an embedded item
|
||||
var sessionID = item.substr(0, slashIndex);
|
||||
var session = Zotero.Integration.sessions[sessionID]
|
||||
if(session) {
|
||||
var embeddedCitation = session.embeddedItems[item.substr(slashIndex+1)];
|
||||
if(embeddedCitation) {
|
||||
embeddedCitation.id = item;
|
||||
return embeddedCitation;
|
||||
/**
|
||||
* Utility functions for dealing with citations
|
||||
* @namespace
|
||||
*/
|
||||
Zotero.Cite = {
|
||||
/**
|
||||
* Locator labels
|
||||
*/
|
||||
"labels":["page", "book", "chapter", "column", "figure", "folio",
|
||||
"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
|
||||
"volume", "verse"],
|
||||
|
||||
/**
|
||||
* Remove specified item IDs in-place from a citeproc-js bibliography object returned
|
||||
* by makeBibliography()
|
||||
* @param {bib} citeproc-js bibliography object
|
||||
* @param {Array} itemsToRemove Array of items to remove
|
||||
*/
|
||||
"removeFromBibliography":function(bib, itemsToRemove) {
|
||||
var removeItems = [];
|
||||
for(let i in bib[0].entry_ids) {
|
||||
for(let j in bib[0].entry_ids[i]) {
|
||||
if(itemsToRemove[bib[0].entry_ids[i][j]]) {
|
||||
removeItems.push(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// is an item ID
|
||||
//if(this._cache[item]) return this._cache[item];
|
||||
zoteroItem = Zotero.Items.get(item);
|
||||
}
|
||||
}
|
||||
for(let i=removeItems.length-1; i>=0; i--) {
|
||||
bib[0].entry_ids.splice(removeItems[i], 1);
|
||||
bib[1].splice(removeItems[i], 1);
|
||||
}
|
||||
},
|
||||
|
||||
if(!zoteroItem) {
|
||||
throw "Zotero.Cite.getCSLItem called to wrap a non-item "+item;
|
||||
}
|
||||
|
||||
// don't return URL or accessed information for journal articles if a
|
||||
// pages field exists
|
||||
var itemType = Zotero.ItemTypes.getName(zoteroItem.itemTypeID);
|
||||
var cslType = CSL_TYPE_MAPPINGS[itemType];
|
||||
if(!cslType) cslType = "article";
|
||||
var ignoreURL = ((zoteroItem.getField("accessDate", true, true) || zoteroItem.getField("url", true, true)) &&
|
||||
["journalArticle", "newspaperArticle", "magazineArticle"].indexOf(itemType) !== -1
|
||||
&& zoteroItem.getField("pages")
|
||||
&& !Zotero.Prefs.get("export.citePaperJournalArticleURL"));
|
||||
|
||||
var cslItem = {
|
||||
'id':zoteroItem.id,
|
||||
'type':cslType
|
||||
};
|
||||
|
||||
// get all text variables (there must be a better way)
|
||||
// TODO: does citeproc-js permit short forms?
|
||||
for(var variable in CSL_TEXT_MAPPINGS) {
|
||||
var fields = CSL_TEXT_MAPPINGS[variable];
|
||||
if(variable == "URL" && ignoreURL) continue;
|
||||
for each(var field in fields) {
|
||||
var value = zoteroItem.getField(field, false, true).toString();
|
||||
if(value != "") {
|
||||
// Strip enclosing quotes
|
||||
if(value.match(Zotero.Cite.System._quotedRegexp)) {
|
||||
value = value.substr(1, value.length-2);
|
||||
}
|
||||
cslItem[variable] = value;
|
||||
break;
|
||||
/**
|
||||
* Convert formatting data from citeproc-js bibliography object into explicit format
|
||||
* parameters for RTF or word processors
|
||||
* @param {bib} citeproc-js bibliography object
|
||||
* @return {Object} Bibliography style parameters.
|
||||
*/
|
||||
"getBibliographyFormatParameters":function getBibliographyFormatParameters(bib) {
|
||||
var bibStyle = {"tabStops":[], "indent":0, "firstLineIndent":0,
|
||||
"lineSpacing":(240*bib[0].linespacing),
|
||||
"entrySpacing":(240*bib[0].entryspacing)};
|
||||
if(bib[0].hangingindent) {
|
||||
bibStyle.indent = 720; // 720 twips = 0.5 in
|
||||
bibStyle.firstLineIndent = -720; // -720 twips = -0.5 in
|
||||
} else if(bib[0]["second-field-align"]) {
|
||||
// this is a really sticky issue. the below works for first fields that look like "[1]"
|
||||
// and "1." otherwise, i have no idea. luckily, this will be good enough 99% of the time.
|
||||
var alignAt = 24+bib[0].maxoffset*120;
|
||||
bibStyle.firstLineIndent = -alignAt;
|
||||
if(bib[0]["second-field-align"] == "margin") {
|
||||
bibStyle.tabStops = [0];
|
||||
} else {
|
||||
bibStyle.indent = alignAt;
|
||||
bibStyle.tabStops = [alignAt];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// separate name variables
|
||||
var authorID = Zotero.CreatorTypes.getPrimaryIDForType(zoteroItem.itemTypeID);
|
||||
var creators = zoteroItem.getCreators();
|
||||
for each(var creator in creators) {
|
||||
if(creator.creatorTypeID == authorID) {
|
||||
var creatorType = "author";
|
||||
|
||||
return bibStyle;
|
||||
},
|
||||
|
||||
/**
|
||||
* Makes a formatted bibliography, if the style defines one; otherwise makes a
|
||||
* formatted list of items
|
||||
* @param {Zotero.Style} style The style to use
|
||||
* @param {Zotero.Item[]} items An array of items
|
||||
* @param {String} format The format of the output (html, text, or rtf)
|
||||
* @return {String} Bibliography or item list in specified format
|
||||
*/
|
||||
"makeFormattedBibliographyOrCitationList":function(style, items, format) {
|
||||
var cslEngine = style.csl;
|
||||
cslEngine.setOutputFormat(format);
|
||||
cslEngine.updateItems([item.id for each(item in items)]);
|
||||
|
||||
var bibliography = Zotero.Cite.makeFormattedBibliography(cslEngine, format);
|
||||
if(bibliography) return bibliography;
|
||||
|
||||
var styleClass = style.class;
|
||||
var citations = [cslEngine.appendCitationCluster({"citationItems":[{"id":item.id}], "properties":{}}, true)[0][1]
|
||||
for each(item in items)];
|
||||
|
||||
if(styleClass == "note") {
|
||||
if(format == "html") {
|
||||
return "<ol>\n\t<li>"+citations.join("</li>\n\t<li>")+"</li>\n</ol>";
|
||||
} else if(format == "text") {
|
||||
var output = [];
|
||||
for(var i=0; i<citations.length; i++) {
|
||||
output.push((i+1)+". "+citations[i]+"\r\n");
|
||||
}
|
||||
return output.join("");
|
||||
} else if(format == "rtf") {
|
||||
var output = ["{\\rtf \n{\\*\\listtable{\\list\\listtemplateid1\\listhybrid{\\listlevel"+
|
||||
"\\levelnfc0\\levelnfcn0\\leveljc0\\leveljcn0\\levelfollow0\\levelstartat1"+
|
||||
"\\levelspace360\\levelindent0{\\*\\levelmarker \\{decimal\\}.}{\\leveltext"+
|
||||
"\\leveltemplateid1\\'02\\'00.;}{\\levelnumbers\\'01;}\\fi-360\\li720\\lin720 }"+
|
||||
"{\\listname ;}\\listid1}}\n{\\*\\listoverridetable{\\listoverride\\listid1"+
|
||||
"\\listoverridecount0\\ls1}}\n\\tx720\\li720\\fi-480\\ls1\\ilvl0\n"];
|
||||
for(var i=0; i<citations.length; i++) {
|
||||
output.push("{\\listtext "+(i+1)+". }"+citations[i]+"\\\n");
|
||||
}
|
||||
output.push("}");
|
||||
return output.join("");
|
||||
} else {
|
||||
throw "Unimplemented bibliography format "+format;
|
||||
}
|
||||
} else {
|
||||
var creatorType = Zotero.CreatorTypes.getName(creator.creatorTypeID);
|
||||
if(format == "html") {
|
||||
return citations.join("<br />");
|
||||
} else if(format == "text") {
|
||||
return citations.join("\r\n");
|
||||
} else if(format == "rtf") {
|
||||
return "<\\rtf \n"+citations.join("\\\n")+"\n}";
|
||||
}
|
||||
}
|
||||
|
||||
var creatorType = CSL_NAMES_MAPPINGS[creatorType];
|
||||
if(!creatorType) continue;
|
||||
|
||||
var nameObj = {'family':creator.ref.lastName, 'given':creator.ref.firstName};
|
||||
|
||||
if(cslItem[creatorType]) {
|
||||
cslItem[creatorType].push(nameObj);
|
||||
} else {
|
||||
cslItem[creatorType] = [nameObj];
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// get date variables
|
||||
for(var variable in CSL_DATE_MAPPINGS) {
|
||||
var date = zoteroItem.getField(CSL_DATE_MAPPINGS[variable], false, true);
|
||||
if(date) {
|
||||
var dateObj = Zotero.Date.strToDate(date);
|
||||
// otherwise, use date-parts
|
||||
var dateParts = [];
|
||||
if(dateObj.year) {
|
||||
// add year, month, and day, if they exist
|
||||
dateParts.push(dateObj.year);
|
||||
if(dateObj.month !== undefined) {
|
||||
dateParts.push(dateObj.month+1);
|
||||
if(dateObj.day) {
|
||||
dateParts.push(dateObj.day);
|
||||
/**
|
||||
* Makes a formatted bibliography
|
||||
* @param {Zotero.Style} style The style
|
||||
* @param {String} format The format of the output (html, text, or rtf)
|
||||
* @return {String} Bibliography in specified format
|
||||
*/
|
||||
"makeFormattedBibliography":function makeFormattedBibliography(cslEngine, format) {
|
||||
cslEngine.setOutputFormat(format);
|
||||
var bib = cslEngine.makeBibliography();
|
||||
if(!bib) return false;
|
||||
|
||||
if(format == "html") {
|
||||
var output = [bib[0].bibstart];
|
||||
for(var i in bib[1]) {
|
||||
output.push(bib[1][i]);
|
||||
|
||||
// add COinS
|
||||
for each(var itemID in bib[0].entry_ids[i]) {
|
||||
try {
|
||||
var co = Zotero.OpenURL.createContextObject(Zotero.Items.get(itemID), "1.0");
|
||||
if(!co) continue;
|
||||
output.push(' <span class="Z3988" title="'+
|
||||
co.replace("&", "&", "g").replace("<", "<", "g").replace(">", ">", "g")+
|
||||
'"/>\n');
|
||||
} catch(e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
}
|
||||
cslItem[variable] = {"date-parts":[dateParts]};
|
||||
}
|
||||
output.push(bib[0].bibend);
|
||||
var html = output.join("");
|
||||
|
||||
var inlineCSS = true;
|
||||
if (!inlineCSS) {
|
||||
return html;
|
||||
}
|
||||
|
||||
//Zotero.debug("maxoffset: " + bib[0].maxoffset);
|
||||
//Zotero.debug("entryspacing: " + bib[0].entryspacing);
|
||||
//Zotero.debug("linespacing: " + bib[0].linespacing);
|
||||
//Zotero.debug("hangingindent: " + bib[0].hangingindent);
|
||||
//Zotero.debug("second-field-align: " + bib[0]["second-field-align"]);
|
||||
|
||||
var maxOffset = parseInt(bib[0].maxoffset);
|
||||
var entrySpacing = parseInt(bib[0].entryspacing);
|
||||
var lineSpacing = parseInt(bib[0].linespacing);
|
||||
var hangingIndent = parseInt(bib[0].hangingindent);
|
||||
var secondFieldAlign = bib[0]["second-field-align"];
|
||||
|
||||
// Validate input
|
||||
if(maxOffset == NaN) throw "Invalid maxoffset";
|
||||
if(entrySpacing == NaN) throw "Invalid entryspacing";
|
||||
if(lineSpacing == NaN) throw "Invalid linespacing";
|
||||
|
||||
var str;
|
||||
try {
|
||||
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||
.createInstance(Components.interfaces.nsIDOMParser),
|
||||
doc = parser.parseFromString(html, "application/xml");
|
||||
|
||||
// if no month, use season as month
|
||||
if(dateObj.part && !dateObj.month) {
|
||||
cslItem[variable].season = dateObj.part;
|
||||
var leftMarginDivs = Zotero.Utilities.xpath(doc, '//div[@class="csl-left-margin"]'),
|
||||
multiField = !!leftMarginDivs.length,
|
||||
clearEntries = multiField;
|
||||
|
||||
// One of the characters is usually a period, so we can adjust this down a bit
|
||||
maxOffset = Math.max(1, maxOffset - 2);
|
||||
|
||||
// Force a minimum line height
|
||||
if(lineSpacing <= 1.35) lineSpacing = 1.35;
|
||||
|
||||
var style = doc.documentElement.getAttribute("style");
|
||||
if(!style) style = "";
|
||||
style += "line-height: " + lineSpacing + "; ";
|
||||
|
||||
if(hangingIndent) {
|
||||
if (multiField && !secondFieldAlign) {
|
||||
throw ("second-field-align=false and hangingindent=true combination is not currently supported");
|
||||
}
|
||||
// If only one field, apply hanging indent on root
|
||||
else if (!multiField) {
|
||||
style += "padding-left: " + hangingIndent + "em; text-indent:-" + hangingIndent + "em;";
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// if no year, pass date literally
|
||||
cslItem[variable] = {"literal":date};
|
||||
|
||||
if(style) doc.documentElement.setAttribute("style", style);
|
||||
|
||||
// csl-entry
|
||||
var divs = Zotero.Utilities.xpath(doc, '//div[@class="csl-entry"]');
|
||||
for(var i=0, n=divs.length; i<n; i++) {
|
||||
var div = divs[i],
|
||||
divStyle = div.getAttribute("style");
|
||||
if(!divStyle) divStyle = "";
|
||||
|
||||
if (clearEntries) {
|
||||
divStyle += "clear: left; ";
|
||||
}
|
||||
|
||||
if(entrySpacing && i !== n - 1) {
|
||||
divStyle += "margin-bottom: " + entrySpacing + "em;";
|
||||
}
|
||||
|
||||
if(divStyle) div.setAttribute("style", divStyle);
|
||||
}
|
||||
|
||||
// Padding on the label column, which we need to include when
|
||||
// calculating offset of right column
|
||||
var rightPadding = .5;
|
||||
|
||||
// div.csl-left-margin
|
||||
for each(var div in leftMarginDivs) {
|
||||
var divStyle = div.getAttribute("style");
|
||||
if(!divStyle) divStyle = "";
|
||||
|
||||
divStyle = "float: left; padding-right: " + rightPadding + "em;";
|
||||
|
||||
// Right-align the labels if aligning second line, since it looks
|
||||
// better and we don't need the second line of text to align with
|
||||
// the left edge of the label
|
||||
if (secondFieldAlign) {
|
||||
divStyle += "text-align: right; width: " + maxOffset + "em;";
|
||||
}
|
||||
|
||||
div.setAttribute("style", divStyle);
|
||||
}
|
||||
|
||||
// div.csl-right-inline
|
||||
for each(var div in Zotero.Utilities.xpath(doc, '//div[@class="csl-right-inline"]')) {
|
||||
var divStyle = div.getAttribute("style");
|
||||
if(!divStyle) divStyle = "";
|
||||
|
||||
divStyle = "margin: 0 .4em 0 " + (secondFieldAlign ? maxOffset + rightPadding : "0") + "em;";
|
||||
|
||||
if (hangingIndent) {
|
||||
divSstyle += "padding-left: " + hangingIndent + "em; text-indent:-" + hangingIndent + "em;";
|
||||
}
|
||||
|
||||
div.setAttribute("style", divStyle);
|
||||
}
|
||||
|
||||
// div.csl-indent
|
||||
for each(var div in Zotero.Utilities.xpath(doc, '//div[@class="csl-indent"]')) {
|
||||
div.setAttribute("style", "margin: .5em 0 0 2em; padding: 0 0 .2em .5em; border-left: 5px solid #ccc;");
|
||||
}
|
||||
|
||||
//Zotero.debug(xml);
|
||||
var s = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"]
|
||||
.createInstance(Components.interfaces.nsIDOMSerializer);
|
||||
str = s.serializeToString(doc);
|
||||
} finally {
|
||||
XML.prettyPrinting = true;
|
||||
XML.ignoreWhitespace = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//this._cache[zoteroItem.id] = cslItem;
|
||||
return cslItem;
|
||||
};
|
||||
|
||||
Zotero.Cite.System.retrieveLocale = function(lang) {
|
||||
var protHandler = Components.classes["@mozilla.org/network/protocol;1?name=chrome"]
|
||||
.createInstance(Components.interfaces.nsIProtocolHandler);
|
||||
try {
|
||||
var channel = protHandler.newChannel(protHandler.newURI("chrome://zotero/content/locale/csl/locales-"+lang+".xml", "UTF-8", null));
|
||||
var rawStream = channel.open();
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
var converterStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterInputStream);
|
||||
converterStream.init(rawStream, "UTF-8", 65535,
|
||||
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
var str = {};
|
||||
converterStream.readString(channel.contentLength, str);
|
||||
converterStream.close();
|
||||
return str.value;
|
||||
};
|
||||
|
||||
Zotero.Cite.System.getAbbreviations = function() {
|
||||
return {};
|
||||
}
|
||||
|
||||
Zotero.Cite.removeFromBibliography = function(bib, itemsToRemove) {
|
||||
var removeItems = [];
|
||||
for(let i in bib[0].entry_ids) {
|
||||
for(let j in bib[0].entry_ids[i]) {
|
||||
if(itemsToRemove[bib[0].entry_ids[i][j]]) {
|
||||
removeItems.push(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
for(let i=removeItems.length-1; i>=0; i--) {
|
||||
bib[0].entry_ids.splice(removeItems[i], 1);
|
||||
bib[1].splice(removeItems[i], 1);
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Cite.getBibliographyFormatParameters = function(bib) {
|
||||
var bibStyle = {"tabStops":[], "indent":0, "firstLineIndent":0,
|
||||
"lineSpacing":(240*bib[0].linespacing),
|
||||
"entrySpacing":(240*bib[0].entryspacing)};
|
||||
if(bib[0].hangingindent) {
|
||||
bibStyle.indent = 720; // 720 twips = 0.5 in
|
||||
bibStyle.firstLineIndent = -720; // -720 twips = -0.5 in
|
||||
} else if(bib[0]["second-field-align"]) {
|
||||
// this is a really sticky issue. the below works for first fields that look like "[1]"
|
||||
// and "1." otherwise, i have no idea. luckily, this will be good enough 99% of the time.
|
||||
var alignAt = 24+bib[0].maxoffset*120;
|
||||
bibStyle.firstLineIndent = -alignAt;
|
||||
if(bib[0]["second-field-align"] == "margin") {
|
||||
bibStyle.tabStops = [0];
|
||||
} else {
|
||||
bibStyle.indent = alignAt;
|
||||
bibStyle.tabStops = [alignAt];
|
||||
}
|
||||
}
|
||||
|
||||
return bibStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a formatted bibliography, if the style defines one; otherwise makes a formatted list of
|
||||
* items
|
||||
* @param {Zotero.Style} style The style to use
|
||||
* @param {Zotero.Item[]} items An array of items
|
||||
* @param {String} format The format of the output
|
||||
*/
|
||||
Zotero.Cite.makeFormattedBibliographyOrCitationList = function(style, items, format) {
|
||||
var cslEngine = style.csl;
|
||||
cslEngine.setOutputFormat(format);
|
||||
cslEngine.updateItems([item.id for each(item in items)]);
|
||||
|
||||
var bibliography = Zotero.Cite.makeFormattedBibliography(cslEngine, format);
|
||||
if(bibliography) return bibliography;
|
||||
|
||||
var styleClass = style.class;
|
||||
var citations = [cslEngine.appendCitationCluster({"citationItems":[{"id":item.id}], "properties":{}}, true)[0][1]
|
||||
for each(item in items)];
|
||||
|
||||
if(styleClass == "note") {
|
||||
if(format == "html") {
|
||||
return "<ol>\n\t<li>"+citations.join("</li>\n\t<li>")+"</li>\n</ol>";
|
||||
|
||||
return str;
|
||||
} else if(format == "text") {
|
||||
var output = [];
|
||||
for(var i=0; i<citations.length; i++) {
|
||||
output.push((i+1)+". "+citations[i]+"\r\n");
|
||||
}
|
||||
return output.join("");
|
||||
return bib[0].bibstart+bib[1].join("")+bib[0].bibend;
|
||||
} else if(format == "rtf") {
|
||||
var output = ["{\\rtf \n{\\*\\listtable{\\list\\listtemplateid1\\listhybrid{\\listlevel"+
|
||||
"\\levelnfc0\\levelnfcn0\\leveljc0\\leveljcn0\\levelfollow0\\levelstartat1"+
|
||||
"\\levelspace360\\levelindent0{\\*\\levelmarker \\{decimal\\}.}{\\leveltext"+
|
||||
"\\leveltemplateid1\\'02\\'00.;}{\\levelnumbers\\'01;}\\fi-360\\li720\\lin720 }"+
|
||||
"{\\listname ;}\\listid1}}\n{\\*\\listoverridetable{\\listoverride\\listid1"+
|
||||
"\\listoverridecount0\\ls1}}\n\\tx720\\li720\\fi-480\\ls1\\ilvl0\n"];
|
||||
for(var i=0; i<citations.length; i++) {
|
||||
output.push("{\\listtext "+(i+1)+". }"+citations[i]+"\\\n");
|
||||
}
|
||||
output.push("}");
|
||||
return output.join("");
|
||||
var bibStyle = Zotero.Cite.getBibliographyFormatParameters(bib);
|
||||
|
||||
var preamble = (bibStyle.tabStops.length ? "\\tx"+bibStyle.tabStops.join(" \\tx")+" " : "");
|
||||
preamble += "\\li"+bibStyle.indent+" \\fi"+bibStyle.firstLineIndent+" "
|
||||
+"\\sl"+bibStyle.lineSpacing+" \\slmult1 "
|
||||
+"\\sa"+bibStyle.entrySpacing+" ";
|
||||
|
||||
return bib[0].bibstart+preamble+bib[1].join("\\\r\n")+"\\\r\n"+bib[0].bibend;
|
||||
} else {
|
||||
throw "Unimplemented bibliography format "+format;
|
||||
}
|
||||
} else {
|
||||
if(format == "html") {
|
||||
return citations.join("<br />");
|
||||
} else if(format == "text") {
|
||||
return citations.join("\r\n");
|
||||
} else if(format == "rtf") {
|
||||
return "<\\rtf \n"+citations.join("\\\n")+"\n}";
|
||||
},
|
||||
|
||||
/**
|
||||
* Get an item by ID, either by retrieving it from the library or looking for the document it
|
||||
* belongs to.
|
||||
* @param {String|Number|Array} id
|
||||
* @return {Zotero.Item} item
|
||||
*/
|
||||
"getItem":function getItem(id) {
|
||||
var slashIndex;
|
||||
|
||||
if(id instanceof Array) {
|
||||
return [Zotero.Cite.getItem(anId) for each(anId in id)];
|
||||
} else if(typeof id === "string" && (slashIndex = id.indexOf("/")) !== -1) {
|
||||
var sessionID = id.substr(0, slashIndex),
|
||||
session = Zotero.Integration.sessions[sessionID],
|
||||
item;
|
||||
if(session) {
|
||||
item = session.embeddedZoteroItems[id.substr(slashIndex+1)];
|
||||
}
|
||||
|
||||
if(!item) {
|
||||
item = new Zotero.Item("document");
|
||||
item.setField("title", "Missing Item");
|
||||
Zotero.log("CSL item "+id+" not found");
|
||||
}
|
||||
return item;
|
||||
} else {
|
||||
return Zotero.Items.get(id);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Makes a formatted bibliography
|
||||
* @param {Zotero.Style} style The style
|
||||
* @param {Zotero.Item[]} items An array of items
|
||||
* citeproc-js system object
|
||||
* @namespace
|
||||
*/
|
||||
Zotero.Cite.makeFormattedBibliography = function(cslEngine, format) {
|
||||
cslEngine.setOutputFormat(format);
|
||||
var bib = cslEngine.makeBibliography();
|
||||
if(!bib) return false;
|
||||
Zotero.Cite.System = {
|
||||
/**
|
||||
* citeproc-js system function for getting items
|
||||
* See http://gsl-nagoya-u.net/http/pub/citeproc-doc.html#retrieveitem
|
||||
* @param {String|Integer} Item ID, or string item for embedded citations
|
||||
* @return {Object} citeproc-js item
|
||||
*/
|
||||
"retrieveItem":function retrieveItem(item) {
|
||||
var zoteroItem, slashIndex;
|
||||
if(item instanceof Zotero.Item) {
|
||||
//if(this._cache[item.id]) return this._cache[item.id];
|
||||
zoteroItem = item;
|
||||
} else {
|
||||
var type = typeof item;
|
||||
if(type === "string" && (slashIndex = item.indexOf("/")) !== -1) {
|
||||
// is an embedded item
|
||||
var sessionID = item.substr(0, slashIndex);
|
||||
var session = Zotero.Integration.sessions[sessionID]
|
||||
if(session) {
|
||||
var embeddedCitation = session.embeddedItems[item.substr(slashIndex+1)];
|
||||
if(embeddedCitation) {
|
||||
embeddedCitation.id = item;
|
||||
return embeddedCitation;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// is an item ID
|
||||
//if(this._cache[item]) return this._cache[item];
|
||||
zoteroItem = Zotero.Items.get(item);
|
||||
}
|
||||
}
|
||||
|
||||
if(format == "html") {
|
||||
var output = [bib[0].bibstart];
|
||||
for(var i in bib[1]) {
|
||||
output.push(bib[1][i]);
|
||||
|
||||
// add COinS
|
||||
for each(var itemID in bib[0].entry_ids[i]) {
|
||||
try {
|
||||
var co = Zotero.OpenURL.createContextObject(Zotero.Items.get(itemID), "1.0");
|
||||
if(!co) continue;
|
||||
output.push(' <span class="Z3988" title="'+
|
||||
co.replace("&", "&", "g").replace("<", "<", "g").replace(">", ">", "g")+
|
||||
'"/>\n');
|
||||
} catch(e) {
|
||||
Zotero.logError(e);
|
||||
if(!zoteroItem) {
|
||||
throw "Zotero.Cite.getCSLItem called to wrap a non-item "+item;
|
||||
}
|
||||
|
||||
// don't return URL or accessed information for journal articles if a
|
||||
// pages field exists
|
||||
var itemType = Zotero.ItemTypes.getName(zoteroItem.itemTypeID);
|
||||
var cslType = CSL_TYPE_MAPPINGS[itemType];
|
||||
if(!cslType) cslType = "article";
|
||||
var ignoreURL = ((zoteroItem.getField("accessDate", true, true) || zoteroItem.getField("url", true, true)) &&
|
||||
["journalArticle", "newspaperArticle", "magazineArticle"].indexOf(itemType) !== -1
|
||||
&& zoteroItem.getField("pages")
|
||||
&& !Zotero.Prefs.get("export.citePaperJournalArticleURL"));
|
||||
|
||||
var cslItem = {
|
||||
'id':zoteroItem.id,
|
||||
'type':cslType
|
||||
};
|
||||
|
||||
// get all text variables (there must be a better way)
|
||||
// TODO: does citeproc-js permit short forms?
|
||||
for(var variable in CSL_TEXT_MAPPINGS) {
|
||||
var fields = CSL_TEXT_MAPPINGS[variable];
|
||||
if(variable == "URL" && ignoreURL) continue;
|
||||
for each(var field in fields) {
|
||||
var value = zoteroItem.getField(field, false, true).toString();
|
||||
if(value != "") {
|
||||
// Strip enclosing quotes
|
||||
if(value.match(/^".+"$/)) {
|
||||
value = value.substr(1, value.length-2);
|
||||
}
|
||||
cslItem[variable] = value;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
output.push(bib[0].bibend);
|
||||
var html = output.join("");
|
||||
|
||||
var inlineCSS = true;
|
||||
if (!inlineCSS) {
|
||||
return html;
|
||||
// separate name variables
|
||||
var authorID = Zotero.CreatorTypes.getPrimaryIDForType(zoteroItem.itemTypeID);
|
||||
var creators = zoteroItem.getCreators();
|
||||
for each(var creator in creators) {
|
||||
if(creator.creatorTypeID == authorID) {
|
||||
var creatorType = "author";
|
||||
} else {
|
||||
var creatorType = Zotero.CreatorTypes.getName(creator.creatorTypeID);
|
||||
}
|
||||
|
||||
var creatorType = CSL_NAMES_MAPPINGS[creatorType];
|
||||
if(!creatorType) continue;
|
||||
|
||||
var nameObj = {'family':creator.ref.lastName, 'given':creator.ref.firstName};
|
||||
|
||||
if(cslItem[creatorType]) {
|
||||
cslItem[creatorType].push(nameObj);
|
||||
} else {
|
||||
cslItem[creatorType] = [nameObj];
|
||||
}
|
||||
}
|
||||
|
||||
//Zotero.debug("maxoffset: " + bib[0].maxoffset);
|
||||
//Zotero.debug("entryspacing: " + bib[0].entryspacing);
|
||||
//Zotero.debug("linespacing: " + bib[0].linespacing);
|
||||
//Zotero.debug("hangingindent: " + bib[0].hangingindent);
|
||||
//Zotero.debug("second-field-align: " + bib[0]["second-field-align"]);
|
||||
// get date variables
|
||||
for(var variable in CSL_DATE_MAPPINGS) {
|
||||
var date = zoteroItem.getField(CSL_DATE_MAPPINGS[variable], false, true);
|
||||
if(date) {
|
||||
var dateObj = Zotero.Date.strToDate(date);
|
||||
// otherwise, use date-parts
|
||||
var dateParts = [];
|
||||
if(dateObj.year) {
|
||||
// add year, month, and day, if they exist
|
||||
dateParts.push(dateObj.year);
|
||||
if(dateObj.month !== undefined) {
|
||||
dateParts.push(dateObj.month+1);
|
||||
if(dateObj.day) {
|
||||
dateParts.push(dateObj.day);
|
||||
}
|
||||
}
|
||||
cslItem[variable] = {"date-parts":[dateParts]};
|
||||
|
||||
// if no month, use season as month
|
||||
if(dateObj.part && !dateObj.month) {
|
||||
cslItem[variable].season = dateObj.part;
|
||||
}
|
||||
} else {
|
||||
// if no year, pass date literally
|
||||
cslItem[variable] = {"literal":date};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var maxOffset = parseInt(bib[0].maxoffset);
|
||||
var entrySpacing = parseInt(bib[0].entryspacing);
|
||||
var lineSpacing = parseInt(bib[0].linespacing);
|
||||
var hangingIndent = parseInt(bib[0].hangingindent);
|
||||
var secondFieldAlign = bib[0]["second-field-align"];
|
||||
|
||||
// Validate input
|
||||
if(maxOffset == NaN) throw "Invalid maxoffset";
|
||||
if(entrySpacing == NaN) throw "Invalid entryspacing";
|
||||
if(lineSpacing == NaN) throw "Invalid linespacing";
|
||||
|
||||
var str;
|
||||
//this._cache[zoteroItem.id] = cslItem;
|
||||
return cslItem;
|
||||
},
|
||||
|
||||
/**
|
||||
* citeproc-js system function for getting locale
|
||||
* See http://gsl-nagoya-u.net/http/pub/citeproc-doc.html#retrieveLocale
|
||||
* @param {String} lang Language to look for a locale for
|
||||
* @return {String|Boolean} The locale as a string if it exists, or false if it doesn't
|
||||
*/
|
||||
"retrieveLocale":function retrieveLocale(lang) {
|
||||
var protHandler = Components.classes["@mozilla.org/network/protocol;1?name=chrome"]
|
||||
.createInstance(Components.interfaces.nsIProtocolHandler);
|
||||
try {
|
||||
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
|
||||
.createInstance(Components.interfaces.nsIDOMParser),
|
||||
doc = parser.parseFromString(html, "application/xml");
|
||||
|
||||
var leftMarginDivs = Zotero.Utilities.xpath(doc, '//div[@class="csl-left-margin"]'),
|
||||
multiField = !!leftMarginDivs.length,
|
||||
clearEntries = multiField;
|
||||
|
||||
// One of the characters is usually a period, so we can adjust this down a bit
|
||||
maxOffset = Math.max(1, maxOffset - 2);
|
||||
|
||||
// Force a minimum line height
|
||||
if(lineSpacing <= 1.35) lineSpacing = 1.35;
|
||||
|
||||
var style = doc.documentElement.getAttribute("style");
|
||||
if(!style) style = "";
|
||||
style += "line-height: " + lineSpacing + "; ";
|
||||
|
||||
if(hangingIndent) {
|
||||
if (multiField && !secondFieldAlign) {
|
||||
throw ("second-field-align=false and hangingindent=true combination is not currently supported");
|
||||
}
|
||||
// If only one field, apply hanging indent on root
|
||||
else if (!multiField) {
|
||||
style += "padding-left: " + hangingIndent + "em; text-indent:-" + hangingIndent + "em;";
|
||||
}
|
||||
}
|
||||
|
||||
if(style) doc.documentElement.setAttribute("style", style);
|
||||
|
||||
// csl-entry
|
||||
var divs = Zotero.Utilities.xpath(doc, '//div[@class="csl-entry"]');
|
||||
for(var i=0, n=divs.length; i<n; i++) {
|
||||
var div = divs[i],
|
||||
divStyle = div.getAttribute("style");
|
||||
if(!divStyle) divStyle = "";
|
||||
|
||||
if (clearEntries) {
|
||||
divStyle += "clear: left; ";
|
||||
}
|
||||
|
||||
if(entrySpacing && i !== n - 1) {
|
||||
divStyle += "margin-bottom: " + entrySpacing + "em;";
|
||||
}
|
||||
|
||||
if(divStyle) div.setAttribute("style", divStyle);
|
||||
}
|
||||
|
||||
// Padding on the label column, which we need to include when
|
||||
// calculating offset of right column
|
||||
var rightPadding = .5;
|
||||
|
||||
// div.csl-left-margin
|
||||
for each(var div in leftMarginDivs) {
|
||||
var divStyle = div.getAttribute("style");
|
||||
if(!divStyle) divStyle = "";
|
||||
|
||||
divStyle = "float: left; padding-right: " + rightPadding + "em;";
|
||||
|
||||
// Right-align the labels if aligning second line, since it looks
|
||||
// better and we don't need the second line of text to align with
|
||||
// the left edge of the label
|
||||
if (secondFieldAlign) {
|
||||
divStyle += "text-align: right; width: " + maxOffset + "em;";
|
||||
}
|
||||
|
||||
div.setAttribute("style", divStyle);
|
||||
}
|
||||
|
||||
// div.csl-right-inline
|
||||
for each(var div in Zotero.Utilities.xpath(doc, '//div[@class="csl-right-inline"]')) {
|
||||
var divStyle = div.getAttribute("style");
|
||||
if(!divStyle) divStyle = "";
|
||||
|
||||
divStyle = "margin: 0 .4em 0 " + (secondFieldAlign ? maxOffset + rightPadding : "0") + "em;";
|
||||
|
||||
if (hangingIndent) {
|
||||
divSstyle += "padding-left: " + hangingIndent + "em; text-indent:-" + hangingIndent + "em;";
|
||||
}
|
||||
|
||||
div.setAttribute("style", divStyle);
|
||||
}
|
||||
|
||||
// div.csl-indent
|
||||
for each(var div in Zotero.Utilities.xpath(doc, '//div[@class="csl-indent"]')) {
|
||||
div.setAttribute("style", "margin: .5em 0 0 2em; padding: 0 0 .2em .5em; border-left: 5px solid #ccc;");
|
||||
}
|
||||
|
||||
//Zotero.debug(xml);
|
||||
var s = Components.classes["@mozilla.org/xmlextras/xmlserializer;1"]
|
||||
.createInstance(Components.interfaces.nsIDOMSerializer);
|
||||
str = s.serializeToString(doc);
|
||||
} finally {
|
||||
XML.prettyPrinting = true;
|
||||
XML.ignoreWhitespace = true;
|
||||
var channel = protHandler.newChannel(protHandler.newURI("chrome://zotero/content/locale/csl/locales-"+lang+".xml", "UTF-8", null));
|
||||
var rawStream = channel.open();
|
||||
} catch(e) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return str;
|
||||
} else if(format == "text") {
|
||||
return bib[0].bibstart+bib[1].join("")+bib[0].bibend;
|
||||
} else if(format == "rtf") {
|
||||
var bibStyle = Zotero.Cite.getBibliographyFormatParameters(bib);
|
||||
|
||||
var preamble = (bibStyle.tabStops.length ? "\\tx"+bibStyle.tabStops.join(" \\tx")+" " : "");
|
||||
preamble += "\\li"+bibStyle.indent+" \\fi"+bibStyle.firstLineIndent+" "
|
||||
+"\\sl"+bibStyle.lineSpacing+" \\slmult1 "
|
||||
+"\\sa"+bibStyle.entrySpacing+" ";
|
||||
|
||||
return bib[0].bibstart+preamble+bib[1].join("\\\r\n")+"\\\r\n"+bib[0].bibend;
|
||||
} else {
|
||||
throw "Unimplemented bibliography format "+format;
|
||||
}
|
||||
}
|
||||
var converterStream = Components.classes["@mozilla.org/intl/converter-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIConverterInputStream);
|
||||
converterStream.init(rawStream, "UTF-8", 65535,
|
||||
Components.interfaces.nsIConverterInputStream.DEFAULT_REPLACEMENT_CHARACTER);
|
||||
var str = {};
|
||||
converterStream.readString(channel.contentLength, str);
|
||||
converterStream.close();
|
||||
return str.value;
|
||||
},
|
||||
|
||||
/**
|
||||
* Get an item by ID, either by retrieving it from the library or looking for the document it
|
||||
* belongs to.
|
||||
* @param {String|Number|Array} id
|
||||
*/
|
||||
Zotero.Cite.getItem = function(id) {
|
||||
var slashIndex;
|
||||
|
||||
if(id instanceof Array) {
|
||||
return [Zotero.Cite.getItem(anId) for each(anId in id)];
|
||||
} else if(typeof id === "string" && (slashIndex = id.indexOf("/")) !== -1) {
|
||||
var sessionID = id.substr(0, slashIndex),
|
||||
session = Zotero.Integration.sessions[sessionID],
|
||||
item;
|
||||
if(session) {
|
||||
item = session.embeddedZoteroItems[id.substr(slashIndex+1)];
|
||||
}
|
||||
|
||||
if(!item) {
|
||||
item = new Zotero.Item("document");
|
||||
item.setField("title", "Missing Item");
|
||||
Zotero.log("CSL item "+id+" not found");
|
||||
}
|
||||
return item;
|
||||
} else {
|
||||
return Zotero.Items.get(id);
|
||||
/**
|
||||
* citeproc-js system function for getting abbreviations
|
||||
* See http://gsl-nagoya-u.net/http/pub/citeproc-doc.html#getabbreviations
|
||||
* Not currently used because it doesn't scale well to large lists
|
||||
*/
|
||||
"getAbbreviations":function getAbbreviations() {
|
||||
return {};
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Cite.labels = ["page", "book", "chapter", "column", "figure", "folio",
|
||||
"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
|
||||
"volume", "verse"];
|
||||
};
|
Loading…
Reference in New Issue
Block a user