Restructure and comment cite.js

This commit is contained in:
Simon Kornblith 2012-07-14 22:26:29 -04:00
parent 0deb2573cc
commit de2d1669fe

View File

@ -1,442 +1,481 @@
Zotero.Cite = function(){} /**
Zotero.Cite.System = function(){}; * 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"],
Zotero.Cite.System._quotedRegexp = /^".+"$/; /**
* Remove specified item IDs in-place from a citeproc-js bibliography object returned
// TODO: Clear this cache from time to time * by makeBibliography()
Zotero.Cite.System._cache = new Object(); * @param {bib} citeproc-js bibliography object
* @param {Array} itemsToRemove Array of items to remove
Zotero.Cite.System.retrieveItem = function(item) { */
var zoteroItem, slashIndex; "removeFromBibliography":function(bib, itemsToRemove) {
if(item instanceof Zotero.Item) { var removeItems = [];
//if(this._cache[item.id]) return this._cache[item.id]; for(let i in bib[0].entry_ids) {
zoteroItem = item; for(let j in bib[0].entry_ids[i]) {
} else { if(itemsToRemove[bib[0].entry_ids[i][j]]) {
var type = typeof item; removeItems.push(i);
if(type === "string" && (slashIndex = item.indexOf("/")) !== -1) { break;
// 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);
} }
} 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; * Convert formatting data from citeproc-js bibliography object into explicit format
} * parameters for RTF or word processors
* @param {bib} citeproc-js bibliography object
// don't return URL or accessed information for journal articles if a * @return {Object} Bibliography style parameters.
// pages field exists */
var itemType = Zotero.ItemTypes.getName(zoteroItem.itemTypeID); "getBibliographyFormatParameters":function getBibliographyFormatParameters(bib) {
var cslType = CSL_TYPE_MAPPINGS[itemType]; var bibStyle = {"tabStops":[], "indent":0, "firstLineIndent":0,
if(!cslType) cslType = "article"; "lineSpacing":(240*bib[0].linespacing),
var ignoreURL = ((zoteroItem.getField("accessDate", true, true) || zoteroItem.getField("url", true, true)) && "entrySpacing":(240*bib[0].entryspacing)};
["journalArticle", "newspaperArticle", "magazineArticle"].indexOf(itemType) !== -1 if(bib[0].hangingindent) {
&& zoteroItem.getField("pages") bibStyle.indent = 720; // 720 twips = 0.5 in
&& !Zotero.Prefs.get("export.citePaperJournalArticleURL")); bibStyle.firstLineIndent = -720; // -720 twips = -0.5 in
} else if(bib[0]["second-field-align"]) {
var cslItem = { // this is a really sticky issue. the below works for first fields that look like "[1]"
'id':zoteroItem.id, // and "1." otherwise, i have no idea. luckily, this will be good enough 99% of the time.
'type':cslType var alignAt = 24+bib[0].maxoffset*120;
}; bibStyle.firstLineIndent = -alignAt;
if(bib[0]["second-field-align"] == "margin") {
// get all text variables (there must be a better way) bibStyle.tabStops = [0];
// TODO: does citeproc-js permit short forms? } else {
for(var variable in CSL_TEXT_MAPPINGS) { bibStyle.indent = alignAt;
var fields = CSL_TEXT_MAPPINGS[variable]; bibStyle.tabStops = [alignAt];
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;
} }
} }
}
// separate name variables return bibStyle;
var authorID = Zotero.CreatorTypes.getPrimaryIDForType(zoteroItem.itemTypeID); },
var creators = zoteroItem.getCreators();
for each(var creator in creators) { /**
if(creator.creatorTypeID == authorID) { * Makes a formatted bibliography, if the style defines one; otherwise makes a
var creatorType = "author"; * 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 { } 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; * 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;
var nameObj = {'family':creator.ref.lastName, 'given':creator.ref.firstName}; if(format == "html") {
var output = [bib[0].bibstart];
for(var i in bib[1]) {
output.push(bib[1][i]);
if(cslItem[creatorType]) { // add COinS
cslItem[creatorType].push(nameObj); for each(var itemID in bib[0].entry_ids[i]) {
} else { try {
cslItem[creatorType] = [nameObj]; var co = Zotero.OpenURL.createContextObject(Zotero.Items.get(itemID), "1.0");
} if(!co) continue;
} output.push(' <span class="Z3988" title="'+
co.replace("&", "&amp;", "g").replace("<", "&lt;", "g").replace(">", "&gt;", "g")+
// get date variables '"/>\n');
for(var variable in CSL_DATE_MAPPINGS) { } catch(e) {
var date = zoteroItem.getField(CSL_DATE_MAPPINGS[variable], false, true); Zotero.logError(e);
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]}; }
output.push(bib[0].bibend);
var html = output.join("");
// if no month, use season as month var inlineCSS = true;
if(dateObj.part && !dateObj.month) { if (!inlineCSS) {
cslItem[variable].season = dateObj.part; 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");
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 if(style) doc.documentElement.setAttribute("style", style);
cslItem[variable] = {"literal":date};
// 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 str;
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>";
} else if(format == "text") { } else if(format == "text") {
var output = []; return bib[0].bibstart+bib[1].join("")+bib[0].bibend;
for(var i=0; i<citations.length; i++) {
output.push((i+1)+". "+citations[i]+"\r\n");
}
return output.join("");
} else if(format == "rtf") { } else if(format == "rtf") {
var output = ["{\\rtf \n{\\*\\listtable{\\list\\listtemplateid1\\listhybrid{\\listlevel"+ var bibStyle = Zotero.Cite.getBibliographyFormatParameters(bib);
"\\levelnfc0\\levelnfcn0\\leveljc0\\leveljcn0\\levelfollow0\\levelstartat1"+
"\\levelspace360\\levelindent0{\\*\\levelmarker \\{decimal\\}.}{\\leveltext"+ var preamble = (bibStyle.tabStops.length ? "\\tx"+bibStyle.tabStops.join(" \\tx")+" " : "");
"\\leveltemplateid1\\'02\\'00.;}{\\levelnumbers\\'01;}\\fi-360\\li720\\lin720 }"+ preamble += "\\li"+bibStyle.indent+" \\fi"+bibStyle.firstLineIndent+" "
"{\\listname ;}\\listid1}}\n{\\*\\listoverridetable{\\listoverride\\listid1"+ +"\\sl"+bibStyle.lineSpacing+" \\slmult1 "
"\\listoverridecount0\\ls1}}\n\\tx720\\li720\\fi-480\\ls1\\ilvl0\n"]; +"\\sa"+bibStyle.entrySpacing+" ";
for(var i=0; i<citations.length; i++) {
output.push("{\\listtext "+(i+1)+". }"+citations[i]+"\\\n"); return bib[0].bibstart+preamble+bib[1].join("\\\r\n")+"\\\r\n"+bib[0].bibend;
}
output.push("}");
return output.join("");
} else { } else {
throw "Unimplemented bibliography format "+format; throw "Unimplemented bibliography format "+format;
} }
} else { },
if(format == "html") {
return citations.join("<br />"); /**
} else if(format == "text") { * Get an item by ID, either by retrieving it from the library or looking for the document it
return citations.join("\r\n"); * belongs to.
} else if(format == "rtf") { * @param {String|Number|Array} id
return "<\\rtf \n"+citations.join("\\\n")+"\n}"; * @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 * citeproc-js system object
* @param {Zotero.Style} style The style * @namespace
* @param {Zotero.Item[]} items An array of items
*/ */
Zotero.Cite.makeFormattedBibliography = function(cslEngine, format) { Zotero.Cite.System = {
cslEngine.setOutputFormat(format); /**
var bib = cslEngine.makeBibliography(); * citeproc-js system function for getting items
if(!bib) return false; * 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") { if(!zoteroItem) {
var output = [bib[0].bibstart]; throw "Zotero.Cite.getCSLItem called to wrap a non-item "+item;
for(var i in bib[1]) { }
output.push(bib[1][i]);
// add COinS // don't return URL or accessed information for journal articles if a
for each(var itemID in bib[0].entry_ids[i]) { // pages field exists
try { var itemType = Zotero.ItemTypes.getName(zoteroItem.itemTypeID);
var co = Zotero.OpenURL.createContextObject(Zotero.Items.get(itemID), "1.0"); var cslType = CSL_TYPE_MAPPINGS[itemType];
if(!co) continue; if(!cslType) cslType = "article";
output.push(' <span class="Z3988" title="'+ var ignoreURL = ((zoteroItem.getField("accessDate", true, true) || zoteroItem.getField("url", true, true)) &&
co.replace("&", "&amp;", "g").replace("<", "&lt;", "g").replace(">", "&gt;", "g")+ ["journalArticle", "newspaperArticle", "magazineArticle"].indexOf(itemType) !== -1
'"/>\n'); && zoteroItem.getField("pages")
} catch(e) { && !Zotero.Prefs.get("export.citePaperJournalArticleURL"));
Zotero.logError(e);
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; // separate name variables
if (!inlineCSS) { var authorID = Zotero.CreatorTypes.getPrimaryIDForType(zoteroItem.itemTypeID);
return html; 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); // get date variables
//Zotero.debug("entryspacing: " + bib[0].entryspacing); for(var variable in CSL_DATE_MAPPINGS) {
//Zotero.debug("linespacing: " + bib[0].linespacing); var date = zoteroItem.getField(CSL_DATE_MAPPINGS[variable], false, true);
//Zotero.debug("hangingindent: " + bib[0].hangingindent); if(date) {
//Zotero.debug("second-field-align: " + bib[0]["second-field-align"]); 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]};
var maxOffset = parseInt(bib[0].maxoffset); // if no month, use season as month
var entrySpacing = parseInt(bib[0].entryspacing); if(dateObj.part && !dateObj.month) {
var lineSpacing = parseInt(bib[0].linespacing); cslItem[variable].season = dateObj.part;
var hangingIndent = parseInt(bib[0].hangingindent); }
var secondFieldAlign = bib[0]["second-field-align"]; } else {
// if no year, pass date literally
cslItem[variable] = {"literal":date};
}
}
}
// Validate input //this._cache[zoteroItem.id] = cslItem;
if(maxOffset == NaN) throw "Invalid maxoffset"; return cslItem;
if(entrySpacing == NaN) throw "Invalid entryspacing"; },
if(lineSpacing == NaN) throw "Invalid linespacing";
var str; /**
* 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 { try {
var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"] var channel = protHandler.newChannel(protHandler.newURI("chrome://zotero/content/locale/csl/locales-"+lang+".xml", "UTF-8", null));
.createInstance(Components.interfaces.nsIDOMParser), var rawStream = channel.open();
doc = parser.parseFromString(html, "application/xml"); } catch(e) {
return false;
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 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;
},
return str; /**
} else if(format == "text") { * citeproc-js system function for getting abbreviations
return bib[0].bibstart+bib[1].join("")+bib[0].bibend; * See http://gsl-nagoya-u.net/http/pub/citeproc-doc.html#getabbreviations
} else if(format == "rtf") { * Not currently used because it doesn't scale well to large lists
var bibStyle = Zotero.Cite.getBibliographyFormatParameters(bib); */
"getAbbreviations":function getAbbreviations() {
var preamble = (bibStyle.tabStops.length ? "\\tx"+bibStyle.tabStops.join(" \\tx")+" " : ""); return {};
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;
} }
} };
/**
* 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);
}
}
Zotero.Cite.labels = ["page", "book", "chapter", "column", "figure", "folio",
"issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
"volume", "verse"];