commit de2d1669fea84116028f40c728f84d655ac33fdd
parent 0deb2573cc841494f58341754527409cf8f39a0c
Author: Simon Kornblith <simon@simonster.com>
Date: Sat, 14 Jul 2012 22:26:29 -0400
Restructure and comment cite.js
Diffstat:
1 file changed, 433 insertions(+), 394 deletions(-)
diff --git a/chrome/content/zotero/xpcom/cite.js b/chrome/content/zotero/xpcom/cite.js
@@ -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";
- } else {
- var creatorType = Zotero.CreatorTypes.getName(creator.creatorTypeID);
- }
- var creatorType = CSL_NAMES_MAPPINGS[creatorType];
- if(!creatorType) continue;
+ 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 nameObj = {'family':creator.ref.lastName, 'given':creator.ref.firstName};
+ var bibliography = Zotero.Cite.makeFormattedBibliography(cslEngine, format);
+ if(bibliography) return bibliography;
- 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);
- }
+ 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");
}
- cslItem[variable] = {"date-parts":[dateParts]};
-
- // if no month, use season as month
- if(dateObj.part && !dateObj.month) {
- cslItem[variable].season = dateObj.part;
+ 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 {
- // if no year, pass date literally
- cslItem[variable] = {"literal":date};
- }
- }
- }
-
- //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;
+ throw "Unimplemented bibliography format "+format;
}
- }
- }
- 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") {
- var output = [];
- for(var i=0; i<citations.length; i++) {
- output.push((i+1)+". "+citations[i]+"\r\n");
+ 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}";
}
- 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 {
- 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}";
- }
- }
-}
-
-/**
- * Makes a formatted bibliography
- * @param {Zotero.Style} style The style
- * @param {Zotero.Item[]} items An array of items
- */
-Zotero.Cite.makeFormattedBibliography = function(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);
+ /**
+ * 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);
+ }
}
}
- }
- 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");
-
- var leftMarginDivs = Zotero.Utilities.xpath(doc, '//div[@class="csl-left-margin"]'),
- multiField = !!leftMarginDivs.length,
- clearEntries = multiField;
+ output.push(bib[0].bibend);
+ var html = output.join("");
- // 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 inlineCSS = true;
+ if (!inlineCSS) {
+ return html;
+ }
- var style = doc.documentElement.getAttribute("style");
- if(!style) style = "";
- style += "line-height: " + lineSpacing + "; ";
+ //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"]);
- 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;";
- }
- }
+ 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"];
- if(style) doc.documentElement.setAttribute("style", style);
+ // Validate input
+ if(maxOffset == NaN) throw "Invalid maxoffset";
+ if(entrySpacing == NaN) throw "Invalid entryspacing";
+ if(lineSpacing == NaN) throw "Invalid linespacing";
- // 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 = "";
+ var str;
+ try {
+ var parser = Components.classes["@mozilla.org/xmlextras/domparser;1"]
+ .createInstance(Components.interfaces.nsIDOMParser),
+ doc = parser.parseFromString(html, "application/xml");
- if (clearEntries) {
- divStyle += "clear: left; ";
- }
+ var leftMarginDivs = Zotero.Utilities.xpath(doc, '//div[@class="csl-left-margin"]'),
+ multiField = !!leftMarginDivs.length,
+ clearEntries = multiField;
- if(entrySpacing && i !== n - 1) {
- divStyle += "margin-bottom: " + entrySpacing + "em;";
- }
+ // One of the characters is usually a period, so we can adjust this down a bit
+ maxOffset = Math.max(1, maxOffset - 2);
- 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 = "";
+ // Force a minimum line height
+ if(lineSpacing <= 1.35) lineSpacing = 1.35;
- divStyle = "float: left; padding-right: " + rightPadding + "em;";
+ var style = doc.documentElement.getAttribute("style");
+ if(!style) style = "";
+ style += "line-height: " + lineSpacing + "; ";
- // 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;";
+ 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;";
+ }
}
- 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 = "";
+ 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);
+ }
- divStyle = "margin: 0 .4em 0 " + (secondFieldAlign ? maxOffset + rightPadding : "0") + "em;";
+ // 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);
+ }
- if (hangingIndent) {
- divSstyle += "padding-left: " + hangingIndent + "em; text-indent:-" + hangingIndent + "em;";
+ // 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;");
}
- div.setAttribute("style", divStyle);
+ //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;
}
- // 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;");
- }
+ 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+" ";
- //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;
+ 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
+ * @return {Zotero.Item} item
+ */
+ "getItem":function getItem(id) {
+ var slashIndex;
- 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;
+ 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);
+ }
}
-}
+};
/**
- * 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
+ * citeproc-js system object
+ * @namespace
*/
-Zotero.Cite.getItem = function(id) {
- var slashIndex;
+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(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(!zoteroItem) {
+ throw "Zotero.Cite.getCSLItem called to wrap a non-item "+item;
}
- if(!item) {
- item = new Zotero.Item("document");
- item.setField("title", "Missing Item");
- Zotero.log("CSL item "+id+" not found");
+ // 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;
+ }
+ }
}
- return item;
- } else {
- return Zotero.Items.get(id);
- }
-}
+
+ // 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];
+ }
+ }
+
+ // 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};
+ }
+ }
+ }
+
+ //this._cache[zoteroItem.id] = cslItem;
+ return cslItem;
+ },
-Zotero.Cite.labels = ["page", "book", "chapter", "column", "figure", "folio",
- "issue", "line", "note", "opus", "paragraph", "part", "section", "sub verbo",
- "volume", "verse"];
-\ No newline at end of file
+ /**
+ * 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 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;
+ },
+
+ /**
+ * 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 {};
+ }
+};
+\ No newline at end of file