commit 0c24beee3ff422b31027ebf04b1f3c592eff0d51
parent 97b2abf2f96c555bae1ba4ae27fe4aeca388fa46
Author: Simon Kornblith <simon@simonster.com>
Date: Tue, 29 Aug 2006 04:24:11 +0000
closes #213, add in-text citation support to citation engine
fixes date and et al. handling bugs in citation engine
permits citation of multiple items in Word integration
Diffstat:
5 files changed, 464 insertions(+), 321 deletions(-)
diff --git a/Zotero.dot.dmg b/Zotero.dot.dmg
Binary files differ.
diff --git a/chrome/chromeFiles/content/scholar/xpcom/cite.js b/chrome/chromeFiles/content/scholar/xpcom/cite.js
@@ -6,7 +6,11 @@
default xml namespace = "http://purl.org/net/xbiblio/csl";
Scholar.Cite = new function() {
+ var _lastCSL = null;
+ var _lastStyle = null;
+
this.getBibliography = getBibliography;
+ this.getCitation = getCitation;
this.getStyles = getStyles;
function getStyles() {
@@ -23,21 +27,47 @@ Scholar.Cite = new function() {
return stylesObject;
}
- function getBibliography(cslID, items, format) {
- // get style
- var sql = "SELECT csl FROM csl WHERE cslID = ?";
- var style = Scholar.DB.valueQuery(sql, [cslID]);
+ /*
+ * generates a bibliography
+ */
+ function getBibliography(cslID, items, format) {
+ var csl = _getCSL(cslID);
- // get item arrays
- var itemArrays = new Array();
- for(var i in items) {
- itemArrays.push(items[i].toArray());
- }
+ // set items
+ csl.setItems(items);
- // create a CSL instance
- var cslInstance = new CSL(style);
- // return bibliography
- return cslInstance.createBibliography(itemArrays, format);
+ // generate bibliography
+ return csl.createBibliography(format);
+ }
+
+ /*
+ * generates a bibliography
+ */
+ function getCitation(cslID, item, items, format) {
+ var csl = _getCSL(cslID);
+
+ // set items
+ csl.setItems(items);
+
+ // generate bibliography
+ return csl.createCitation(item, format);
+ }
+
+ /*
+ * gets CSL from the database, or, if it's the most recently used style,
+ * from the cache
+ */
+ function _getCSL(cslID) {
+ if(_lastStyle != cslID) {
+ // get style
+ var sql = "SELECT csl FROM csl WHERE cslID = ?";
+ var style = Scholar.DB.valueQuery(sql, [cslID]);
+
+ // create a CSL instance
+ _lastCSL = new CSL(style);
+ _lastStyle = cslID;
+ }
+ return _lastCSL;
}
}
@@ -57,6 +87,7 @@ CSL = function(csl) {
// load class defaults
this._class = this._csl["@class"].toString();
+ Scholar.debug("CSL: style class is "+this._class);
this._defaults = new Object();
// load class defaults
@@ -68,136 +99,103 @@ CSL = function(csl) {
}
// load defaults from CSL
this._parseFieldDefaults(this._csl.defaults);
+ // parse bibliography options, since these will be necessary for
+ // disambiguation purposes even for citations
+ this._parseBibliographyOptions();
+}
+
+/*
+ * set items - convert to array and pre-process
+ */
+CSL.prototype.setItems = function(items) {
+ var serializedItemString = "";
+ for each(var item in items) {
+ serializedItemString += item.getID()+",";
+ }
- // decide whether to parse bibliography, or, if none exists, citation
- if(this._csl.bibliography.length()) {
- var cite = this._csl.bibliography;
- this._parseBibliographyOptions();
-
- this._itemElement = this._csl.bibliography.layout.item;
- } else {
- var cite = this._csl.citation;
+ Scholar.debug("CSL: items set to "+serializedItemString);
+
+ if(serializedItemString != this._serializedItemString) {
+ // only re-process if there are new items
+ this._serializedItemString = serializedItemString;
+ this._items = items;
+ this._preprocessItems();
+ }
+}
+
+/*
+ * create a citation (in-text or footnote)
+ */
+CSL.prototype.createCitation = function(items, format) {
+ Scholar.debug("CSL: creating citation for item "+items[0].getID());
+ if(!this._cit) {
this._parseCitationOptions();
-
- // find the type item without position="subsequent"
- var itemElements = this._csl.citation.layout.item;
- for each(var itemElement in itemElements) {
- if(itemElement.@position.toString() != "subsequent") {
- this._itemElement = itemElement;
- break;
- }
- }
- if(!this._itemElement) {
- throw("no primary item found in citation. cannot cite.");
+ }
+
+ var string = "";
+ for(var i in items) {
+ if(this._cit.format && this._cit.format.delimiter && string) {
+ // add delimiter if one exists, and this isn't the first element
+ // with content
+ string += this._cit.format.delimiter;
}
+ string += this._getCitation(items[i], format, this._cit);
}
- // create an associative array of available types
- if(this._itemElement.choose) {
- this._types = new Object();
- this._serializations = new Object();
- for each(var type in this._itemElement.choose.type) {
- this._types[type.@name] = true;
- this._serializations[type.@name] = new Object();
+ // add format
+ if(this._cit.format) {
+ // add citation prefix or suffix
+ if(this._cit.format.prefix) {
+ string = this._cit.format.prefix + string ;
+ }
+ if(this._cit.format.suffix) {
+ string += this._cit.format.suffix;
}
- } else {
- // if there's only one type, bind it to index 0
- this._serializations[0] = new Object();
- this._types[0] = this._parseFields(this._itemElement.children(), 0);
}
+
+ return string;
}
/*
* create a bibliography
* (items is expected to be an array of items)
*/
-CSL.prototype.createBibliography = function(items, format) {
- // preprocess items
- this._preprocessItems(items);
-
- // sort by sort order
- if(this._opt.sortOrder) {
- var me = this;
- items.sort(function(a, b) {
- return me._compareItem(a, b);
- });
+CSL.prototype.createBibliography = function(format) {
+ if(!this._bib) {
+ // decide whether to parse bibliography, or, if none exists, citation
+ Scholar.debug("CSL: using citation element");
+ if(!this._cit) {
+ this._parseCitationOptions();
+ }
+ this._bib = this._cit;
}
- // disambiguate items
- this._disambiguateItems(items);
+ // preprocess this._items
+ Scholar.debug("CSL: preprocessing items");
+ this._preprocessItems();
- // process items
+ // process this._items
var output = "";
if(format == "HTML") {
if(this._class == "note") {
output += '<ol>\r\n';
- } else if(this._opt.hangingIndent) {
+ } else if(this._bib.hangingIndent) {
output += '<div style="margin-left:0.5in;text-indent:-0.5in;">\r\n';
}
} else if(format == "RTF") {
var index = 0;
output += "{\\rtf\\ansi{\\fonttbl\\f0\\froman Times New Roman;}{\\colortbl;\\red255\\green255\\blue255;}\\pard\\f0";
- if(this._opt.hangingIndent) {
+ if(this._bib.hangingIndent) {
output += "\\li720\\fi-720";
}
output += "\r\n";
}
- for(var i in items) {
- var item = items[i];
- if(item.itemType == "note" || item.itemType == "file") {
- // skip notes and files
- continue;
- }
-
- // determine mapping
- if(!this._types[0]) { // multiple types
- if(CSL._optionalTypeMappings[item.itemType]
- && this._types[CSL._optionalTypeMappings[item.itemType]]) {
- // try preferred types first
- if(this._types[CSL._optionalTypeMappings[item.itemType]] === true) {
- // exists but not yet processed
- this._parseReferenceType(CSL._optionalTypeMappings[item.itemType]);
- }
-
- var typeName = CSL._optionalTypeMappings[item.itemType];
- } else {
- // otherwise, use fallback types
- if(this._types[CSL._fallbackTypeMappings[item.itemType]] === true) {
- this._parseReferenceType(CSL._fallbackTypeMappings[item.itemType]);
- }
-
- var typeName = CSL._fallbackTypeMappings[item.itemType];
- }
- } else { // only one element
- var typeName = 0;
- }
+ for(var i in this._items) {
+ var item = this._items[i];
- var type = this._types[typeName];
-
- var string = "";
- for(var j in type) {
- var value = this._getFieldValue(type[j].name, type[j], item, format, typeName);
-
- if(this._opt.format && this._opt.format.delimiter && string && value) {
- // add delimiter before if one exists, and this isn't the first
- // element with content
- string += this._opt.format.delimiter;
- }
- string += value;
- }
-
- if(this._opt.format) {
- // add citation prefix or suffix
- if(this._opt.format.prefix) {
- string = string + this._opt.format.prefix;
- }
-
- if(this._opt.format.suffix) {
- string = string + this._opt.format.suffix;
- }
- }
+ var string = this._getCitation(item, format, this._bib);
// add line feeds
if(format == "HTML") {
@@ -225,7 +223,7 @@ CSL.prototype.createBibliography = function(items, format) {
if(format == "HTML") {
if(this._class == "note") {
output += '</ol>';
- } else if(this._opt.hangingIndent) {
+ } else if(this._bib.hangingIndent) {
output += '</div>';
}
} else if(format == "RTF") {
@@ -242,33 +240,6 @@ CSL._months = ["January", "February", "March", "April", "May", "June", "July",
CSL._monthsShort = ["Jan", "Feb", "Mar", "Apr", "May", "Jun",
"Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
-CSL._optionalTypeMappings = {
- journalArticle:"article-journal",
- magazineArticle:"article-magazine",
- newspaperArticle:"article-newspaper",
- thesis:"thesis",
- letter:"personal communication",
- manuscript:"manuscript",
- interview:"interview",
- film:"motion picture",
- artwork:"graphic",
- website:"webpage"
-};
-// TODO: check with Elena/APA/MLA on this
-CSL._fallbackTypeMappings = {
- book:"book",
- bookSection:"chapter",
- journalArticle:"article",
- magazineArticle:"article",
- newspaperArticle:"article",
- thesis:"book",
- letter:"article",
- manuscript:"book",
- interview:"book",
- film:"book",
- artwork:"book",
- website:"article"
-};
// for elements that inherit defaults from each other
CSL._inherit = {
author:"contributor",
@@ -475,6 +446,7 @@ CSL.prototype._parseFieldDefaults = function(ref) {
for each(var element in ref.children()) {
if(element.namespace() == CSL.ns) { // ignore elements in other namespaces
var name = element.localName();
+ Scholar.debug("CSL: parsing field defaults for "+name);
var fieldDesc = this._parseFieldAttrChildren(element);
if(this._defaults[name]) { // inherit from existing defaults
@@ -490,7 +462,7 @@ CSL.prototype._parseFieldDefaults = function(ref) {
/*
* parses a list of fields into an array of objects
*/
-CSL.prototype._parseFields = function(ref, type, noInherit) {
+CSL.prototype._parseFields = function(ref, type, bibCitElement, inheritFormat) {
var typeDesc = new Array();
for each(var element in ref) {
if(element.namespace() == CSL.ns) { // ignore elements in other namespaces
@@ -501,14 +473,16 @@ CSL.prototype._parseFields = function(ref, type, noInherit) {
if(type != undefined) {
var fieldDefaults = this._getFieldDefaults(itemDesc.name);
itemDesc = this._merge(fieldDefaults, itemDesc);
- if(!noInherit) {
- itemDesc = this._merge(this._opt.inheritFormat, itemDesc);
+ if(bibCitElement && inheritFormat) {
+ itemDesc = this._merge(bibCitElement.inheritFormat, itemDesc);
}
// create serialized representation
itemDesc._serialized = this._serializeElement(itemDesc.name, itemDesc);
// add to serialization for type
- this._serializations[type][itemDesc._serialized] = itemDesc;
+ if(bibCitElement) {
+ bibCitElement._serializations[type][itemDesc._serialized] = itemDesc;
+ }
}
// parse group children
@@ -518,7 +492,7 @@ CSL.prototype._parseFields = function(ref, type, noInherit) {
var children = element.children();
if(children.length()) {
- itemDesc.children = this._parseFields(children, type, true);
+ itemDesc.children = this._parseFields(children, type, bibCitElement);
}
} else {
// parse attributes on this field
@@ -531,17 +505,20 @@ CSL.prototype._parseFields = function(ref, type, noInherit) {
return typeDesc;
}
-CSL.prototype._parseEtAl = function(etAl) {
+/*
+ * parses an et al field
+ */
+CSL.prototype._parseEtAl = function(etAl, bibCitElement) {
if(etAl.length()) {
- this._opt.etAl = new Object();
+ bibCitElement.etAl = new Object();
if(etAl.length() > 1) {
// separate first and subsequent et als
for each(var etAlElement in etAl) {
if(etAlElement.@position == "subsequent") {
- this._opt.subsequentEtAl = new Object();
- this._opt.subsequentEtAl.minCreators = parseInt(etAlElement['@min-authors']);
- this._opt.subsequentEtAl.useFirst = parseInt(etAlElement['@use-first']);
+ bibCitElement.subsequentEtAl = new Object();
+ bibCitElement.subsequentEtAl.minCreators = parseInt(etAlElement['@min-authors']);
+ bibCitElement.subsequentEtAl.useFirst = parseInt(etAlElement['@use-first']);
} else {
var parseElement = etAlElement;
}
@@ -550,8 +527,8 @@ CSL.prototype._parseEtAl = function(etAl) {
var parseElement = etAl;
}
- this._opt.etAl.minCreators = parseInt(parseElement['@min-authors']);
- this._opt.etAl.useFirst = parseInt(parseElement['@use-first']);
+ bibCitElement.etAl.minCreators = parseInt(parseElement['@min-authors']);
+ bibCitElement.etAl.useFirst = parseInt(parseElement['@use-first']);
}
}
@@ -561,31 +538,31 @@ CSL.prototype._parseEtAl = function(etAl) {
*/
CSL.prototype._parseBibliographyOptions = function() {
var bibliography = this._csl.bibliography;
- this._opt = new Object();
+ this._bib = new Object();
// global prefix and suffix format information
- this._opt.inheritFormat = new Array();
+ this._bib.inheritFormat = new Array();
for each(var attribute in bibliography.layout.item.attributes()) {
- this._opt.inheritFormat[attribute.name()] = attribute.toString();
+ this._bib.inheritFormat[attribute.name()] = attribute.toString();
}
// sections (TODO)
- this._opt.sections = [{groupBy:"default",
+ this._bib.sections = [{groupBy:"default",
heading:bibliography.layout.heading.text["@term-name"].toString()}];
for each(var section in bibliography.layout.section) {
- this._opt.sections.push([{groupBy:section["@group-by"].toString(),
+ this._bib.sections.push([{groupBy:section["@group-by"].toString(),
heading:section.heading.text["@term-name"].toString()}]);
}
// subsequent author substitute
// replaces subsequent occurances of an author with a given string
if(bibliography['@subsequent-author-substitute']) {
- this._opt.subsequentAuthorSubstitute = bibliography['@subsequent-author-substitute'].toString();
+ this._bib.subsequentAuthorSubstitute = bibliography['@subsequent-author-substitute'].toString();
}
// hanging indent
if(bibliography['@hanging-indent']) {
- this._opt.hangingIndent = true;
+ this._bib.hangingIndent = true;
}
// sort order
@@ -593,23 +570,26 @@ CSL.prototype._parseBibliographyOptions = function() {
if(algorithm) {
// for classes, use the sort order that
if(algorithm == "author-date") {
- this._opt.sortOrder = [this._getFieldDefaults("author"),
+ this._bib.sortOrder = [this._getFieldDefaults("author"),
this._getFieldDefaults("date")];
- this._opt.sortOrder[0].name = "author";
- this._opt.sortOrder[1].name = "date";
+ this._bib.sortOrder[0].name = "author";
+ this._bib.sortOrder[1].name = "date";
} else if(algorithm == "label") {
- this._opt.sortOrder = [this._getFieldDefaults("label")];
- this._opt.sortOrder[0].name = "label";
+ this._bib.sortOrder = [this._getFieldDefaults("label")];
+ this._bib.sortOrder[0].name = "label";
} else if(algorithm == "cited") {
- this._opt.sortOrder = [this._getFieldDefaults("cited")];
- this._opt.sortOrder[0].name = "cited";
+ this._bib.sortOrder = [this._getFieldDefaults("cited")];
+ this._bib.sortOrder[0].name = "cited";
}
} else {
- this._opt.sortOrder = this._parseFields(bibliography.sort, false);
+ this._bib.sortOrder = this._parseFields(bibliography.sort, false, this._bib);
}
// parse et al
- this._parseEtAl(bibliography["use-et_al"]);
+ this._parseEtAl(bibliography["et-al"], this._bib);
+
+ // parse types
+ this._parseTypes(this._csl.bibliography.layout.item, this._bib);
}
/*
@@ -618,24 +598,71 @@ CSL.prototype._parseBibliographyOptions = function() {
*/
CSL.prototype._parseCitationOptions = function() {
var citation = this._csl.citation;
- this._opt = new Object();
+ this._cit = new Object();
// parse et al
- this._parseEtAl(citation["use-et_al"]);
+ this._parseEtAl(citation["et-al"], this._cit);
// global format information
- this._opt.format = new Array();
+ this._cit.format = new Array();
for each(var attribute in citation.attributes()) {
- this._opt.format[attribute.name()] = attribute.toString();
+ this._cit.format[attribute.name()] = attribute.toString();
+ }
+
+ // parse types
+ this._parseTypes(this._csl.citation.layout.item, this._cit);
+}
+
+/*
+ * determine available reference types and add their XML objects to the tree
+ * (they will be parsed on the fly when necessary; see _parseReferenceType)
+ */
+CSL.prototype._parseTypes = function(itemElements, bibCitElement) {
+ Scholar.debug("CSL: parsing item elements");
+
+ bibCitElement._types = new Object();
+ bibCitElement._serializations = new Object();
+
+ // find the type item without position="subsequent"
+ for each(var itemElement in itemElements) {
+ if(itemElement.@position.toString() == "subsequent") {
+ // bind subsequent element to position 1
+ bibCitElement._types[1] = itemElement;
+ bibCitElement._serializations[1] = new Object();
+ } else {
+ // create an associative array of available types
+ if(itemElement.choose.length()) {
+ for each(var type in itemElement.choose.type) {
+ bibCitElement._types[type.@name] = type;
+ bibCitElement._serializations[type.@name] = new Object();
+ }
+ } else {
+ // if there's only one type, bind it to index 0
+ bibCitElement._types[0] = itemElement;
+ bibCitElement._serializations[0] = new Object();
+ }
+ }
}
}
/*
* convert reference types to native structures for speed
*/
-CSL.prototype._parseReferenceType = function(reftype) {
- var ref = this._itemElement.choose.type.(@name==reftype).children();
- this._types[reftype] = this._parseFields(ref, reftype);
+CSL.prototype._getTypeObject = function(reftype, bibCitElement) {
+ if(!bibCitElement._types[reftype]) {
+ // no type available
+ return false;
+ }
+
+ // parse type if necessary
+ if(typeof(bibCitElement._types[reftype]) == "xml") {
+ Scholar.debug("CSL: parsing XML for "+reftype);
+ bibCitElement._types[reftype] = this._parseFields(bibCitElement._types[reftype].children(),
+ reftype, bibCitElement, true);
+ }
+
+ Scholar.debug("CSL: got object for "+reftype);
+ return bibCitElement._types[reftype];
}
/*
@@ -711,23 +738,25 @@ CSL.prototype._processDate = function(string) {
var jsDate = new Date(m[1], m[2]-1, m[3], false, false, false);
} else { // not an sql date
var yearRe = /^[0-9]+$/;
- if(yearRe) { // is a year
+ if(yearRe.test(string)) { // is a year
date.year = string;
return date;
- } else { // who knows what this is
+ } else { // who knows what this is
var jsDate = new Date(string)
}
}
if(isNaN(jsDate.valueOf())) { // couldn't parse
// get year and say other parts are month
- var yearRe = /^(.*)([^0-9]{4})(.*)$/
+ var yearRe = /^(.*)([0-9]{4})(.*)$/
var m = yearRe.exec(string);
- date.year = m[2];
- date.month = m[1]
- if(m[2] && m[3]) date.month += " ";
- date.month += m[3];
+ if(m) {
+ date.year = m[2];
+ date.month = m[1];
+ if(m[2] && m[3]) date.month += " ";
+ date.month += m[3];
+ }
} else {
date.year = jsDate.getFullYear();
date.month = jsDate.getMonth();
@@ -960,56 +989,56 @@ CSL.prototype._lpad = function(string, pad, length) {
* preprocess items, separating authors, editors, and translators arrays into
* separate properties
*/
-CSL.prototype._preprocessItems = function(items) {
- for(var i in items) {
- var item = items[i];
-
- // namespace everything in item._csl so there's no chance of overlap
- item._csl = new Object();
- item._csl.ignore = new Array();
-
- item._csl.authors = new Array();
- item._csl.editors = new Array();
- item._csl.translators = new Array();
+CSL.prototype._preprocessItems = function() {
+ // get data necessary to generate citations before sorting
+ for(var i in this._items) {
+ var item = this._items[i];
- // separate item into authors, editors, translators
- for(var j in item.creators) {
- var creator = item.creators[j];
+ if(!item._csl) {
+ // namespace everything in item._csl so there's no chance of overlap
+ item._csl = new Object();
+ item._csl.ignore = new Array();
- if(creator.creatorType == "editor") {
- item._csl.editors.push(creator);
- } else if(creator.creatorType == "translator") {
- item._csl.translators.push(creator);
- } else if(creator.creatorType == "author") {
- // TODO: do we just ignore contributors?
- item._csl.authors.push(creator);
- }
+ // separate item into authors, editors, translators
+ var creators = this._separateItemCreators(item);
+ item._csl.authors = creators[0];
+ item._csl.editors = creators[1];
+ item._csl.translators = creators[2];
+
+ // parse date
+ item._csl.date = CSL.prototype._processDate(item.getField("date"));
+ } else {
+ // clear disambiguation and subsequent author substitute
+ if(item._csl.disambiguation) item._csl.disambiguation = undefined;
+ if(item._csl.subsequentAuthorSubstitute) item._csl.subsequentAuthorSubstitute = undefined;
}
-
- // parse date
- item._csl.date = CSL.prototype._processDate(item.date);
}
-}
-
-/*
- * disambiguates items, after pre-processing and sorting
- */
-CSL.prototype._disambiguateItems = function(items) {
+
+ // sort by sort order
+ if(this._bib.sortOrder) {
+ Scholar.debug("CSL: sorting this._items");
+ var me = this;
+ this._items.sort(function(a, b) {
+ return me._compareItem(a, b);
+ });
+ }
+
+ // disambiguate items after preprocessing and sorting
var usedCitations = new Array();
var lastAuthor;
- for(var i in items) {
- var item = items[i];
+ for(var i in this._items) {
+ var item = this._items[i];
var author = this._getFieldValue("author",
this._getFieldDefaults("author"),
- item, "disambiguate");
+ item, "disambiguate", this._bib);
// handle (2006a) disambiguation for author-date styles
if(this._class == "author-date") {
var citation = author+" "+this._getFieldValue("date",
this._getFieldDefaults("date"),
- item, "disambiguate");
+ item, "disambiguate", this._bib);
if(usedCitations[citation]) {
if(!usedCitations[citation]._csl.date.disambiguation) {
@@ -1042,7 +1071,7 @@ CSL.prototype._disambiguateItems = function(items) {
item._csl.number = i;
// handle subsequent author substitutes
- if(this._opt.subsequentAuthorSubstitute && lastAuthor == author) {
+ if(this._bib.subsequentAuthorSubstitute && lastAuthor == author) {
item._csl.subsequentAuthorSubstitute = true;
}
lastAuthor = author;
@@ -1053,11 +1082,13 @@ CSL.prototype._disambiguateItems = function(items) {
* handles sorting of items
*/
CSL.prototype._compareItem = function(a, b, opt) {
- for(var i in this._opt.sortOrder) {
- var sortElement = this._opt.sortOrder[i];
+ for(var i in this._bib.sortOrder) {
+ var sortElement = this._bib.sortOrder[i];
- var aValue = this._getFieldValue(sortElement.name, sortElement, a, "compare");
- var bValue = this._getFieldValue(sortElement.name, sortElement, b, "compare");
+ var aValue = this._getFieldValue(sortElement.name, sortElement, a,
+ "compare", this._bib);
+ var bValue = this._getFieldValue(sortElement.name, sortElement, b,
+ "compare", this._bib);
if(bValue > aValue) {
return -1;
} else if(bValue < aValue) {
@@ -1073,7 +1104,7 @@ CSL.prototype._compareItem = function(a, b, opt) {
* process creator objects; if someone had a creator model that handled
* non-Western names better than ours, this would be the function to change
*/
-CSL.prototype._processCreators = function(type, element, creators, format) {
+CSL.prototype._processCreators = function(type, element, creators, format, bibCitElement) {
var maxCreators = creators.length;
if(!maxCreators) return;
@@ -1094,8 +1125,8 @@ CSL.prototype._processCreators = function(type, element, creators, format) {
var useEtAl = false;
// figure out if we need to use "et al"
- if(this._opt.etAl && maxCreators >= this._opt.etAl.minCreators) {
- maxCreators = this._opt.etAl.useFirst;
+ if(bibCitElement.etAl && maxCreators >= bibCitElement.etAl.minCreators) {
+ maxCreators = bibCitElement.etAl.useFirst;
useEtAl = true;
}
@@ -1103,21 +1134,23 @@ CSL.prototype._processCreators = function(type, element, creators, format) {
var authorStrings = [];
var firstName, lastName;
for(var i=0; i<maxCreators; i++) {
- if(typeof(child["initialize-with"]) == "string") {
- // even if initialize-with is simply an empty string, use
- // initials
-
- // use first initials
- var firstName = "";
- var firstNames = creators[i].firstName.split(" ");
- for(var j in firstNames) {
- if(firstNames[j]) {
- // get first initial, put in upper case, add initializeWith string
- firstName += firstNames[j][0].toUpperCase()+child["initialize-with"];
+ var firstName = "";
+ if(element["form"] != "short") {
+ if(child["initialize-with"] != undefined) {
+ // even if initialize-with is simply an empty string, use
+ // initials
+
+ // use first initials
+ var firstNames = creators[i].firstName.split(" ");
+ for(var j in firstNames) {
+ if(firstNames[j]) {
+ // get first initial, put in upper case, add initializeWith string
+ firstName += firstNames[j][0].toUpperCase()+child["initialize-with"];
+ }
}
+ } else {
+ firstName = creators[i].firstName;
}
- } else {
- firstName = creators[i].firstName;
}
lastName = creators[i].lastName;
@@ -1128,9 +1161,9 @@ CSL.prototype._processCreators = function(type, element, creators, format) {
// if this is the first author and name-as-sort="first"
// or if this is a subsequent author and name-as-sort="all"
// then the name gets inverted
- authorStrings.push(lastName+child["sort-separator"]+firstName);
+ authorStrings.push(lastName+(firstName ? child["sort-separator"]+firstName : ""));
} else {
- authorStrings.push(firstName+" "+lastName);
+ authorStrings.push((firstName ? firstName+" " : "")+lastName);
}
}
@@ -1138,7 +1171,7 @@ CSL.prototype._processCreators = function(type, element, creators, format) {
var joinString = ", ";
if(maxCreators > 1) {
if(useEtAl) { // multiple creators and need et al
- authorStrings.push(this._getTerm("et al."));
+ authorStrings.push(this._getTerm("et-al"));
} else { // multiple creators but no et al
// add and to last creator
if(child["and"]) {
@@ -1177,9 +1210,45 @@ CSL.prototype._processCreators = function(type, element, creators, format) {
}
/*
+ * get a citation, given an item and bibCitElement
+ */
+CSL.prototype._getCitation = function(item, format, bibCitElement) {
+ Scholar.debug("CSL: generating citation for item "+item.getID());
+
+ // determine mapping
+ if(bibCitElement._types[0]) {
+ // only one element
+ var typeName = 0;
+ var type = this._getTypeObject(typeName, bibCitElement);
+ } else {
+ var typeNames = this._getTypeFromItem(item);
+ for each(var typeName in typeNames) {
+ var type = this._getTypeObject(typeName, bibCitElement);
+ if(type) {
+ break;
+ }
+ }
+ }
+
+ if(!type) {
+ throw("CSL: ERROR: no type found for item");
+ }
+ Scholar.debug("CSL: using CSL type "+typeName);
+
+ var string = "";
+ for(var j in type) {
+ var value = this._getFieldValue(type[j].name, type[j], item, format,
+ bibCitElement, typeName);
+ string += value;
+ }
+
+ return string;
+}
+
+/*
* processes an element from a (pre-processed) item into text
*/
-CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
+CSL.prototype._getFieldValue = function(name, element, item, format, bibCitElement, typeName) {
var data = "";
if(element._serialized && item._csl.ignore[element._serialized]) {
@@ -1190,16 +1259,16 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
var dontEscape = true;
if(name == "author") {
- if(item._csl.subsequentAuthorSubstitute) {
+ if(item._csl.subsequentAuthorSubstitute && bibCitElement.subsequentAuthorSubstitute) {
// handle subsequent author substitute behavior
- data = this._opt.subsequentAuthorSubstitute;
+ data = bibCitElement.subsequentAuthorSubstitute;
} else {
- data = this._processCreators(name, element, item._csl.authors, format);
+ data = this._processCreators(name, element, item._csl.authors, format, bibCitElement);
}
} else if(name == "editor") {
- data = this._processCreators(name, element, item._csl.editors, format);
+ data = this._processCreators(name, element, item._csl.editors, format, bibCitElement);
} else if(name == "translator") {
- data = this._processCreators(name, element, item._csl.translators, format);
+ data = this._processCreators(name, element, item._csl.translators, format, bibCitElement);
} else if(name == "titles") {
for(var i in element.children) {
var child = element.children[i];
@@ -1208,11 +1277,11 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
if(child.name == "title") { // for now, we only care about the
// "title" sub-element
if(!element.relation) {
- string = item.title;
+ string = item.getField("title");
} else if(element.relation == "container") {
- string = item.publicationTitle;
+ string = item.getField("publicationTitle");
} else if(element.relation == "collection") {
- string = item.seriesTitle;
+ string = item.getField("seriesTitle");
}
}
@@ -1228,9 +1297,9 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
var string = "";
if(child.name == "place") {
- string = item.place;
+ string = item.getField("place");
} else if(child.name == "name") {
- string = item.publisher
+ string = item.getField("publisher");
}
if(string) {
@@ -1245,18 +1314,14 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
var string = "";
if(child.name == "url") {
- // TODO: better URL-handling strategies
- if(item.url) {
- string = item.url;
- }
+ string = item.getField("url");
} else if(child.name == "date") {
- if(item.accessDate) {
- string = this._formatDate(child, this._processDate(item.accessDate), format);
+ var field = item.getField("accessDate");
+ if(field) {
+ string = this._formatDate(child, this._processDate(field), format);
}
} else if(child.name == "physicalLocation") {
- if(item.archiveLocation) {
- string = item.archiveLocation;
- }
+ string = item.getField("archiveLocation");
} else if(child.name == "text") {
string = this._getTerm(child["term-name"]);
}
@@ -1273,26 +1338,23 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
if(!save) {
data = "";
}
- } else if(name == "volume") {
- if(item.volume) {
- data = this._formatLocator("volume", element, item.volume, format);
- }
- } else if(name == "issue") {
- if(item.issue) {
- data = this._formatLocator("issue", element, item.issue, format);
+ } else if(name == "volume" || name == "issue") {
+ var field = item.getField(name);
+ if(field) {
+ data = this._formatLocator(name, element, field, format);
}
} else if(name == "pages") {
- if(item.pages) {
- data = this._formatLocator("page", element, item.pages, format);
+ var field = item.getField("pages");
+ if(field) {
+ data = this._formatLocator("page", element, field, format);
}
} else if(name == "edition") {
- if(item.edition) {
- data = item.edition;
- }
+ data = item.getField("edition");
dontEscape = false;
} else if(name == "genre") {
- if(item.type || item.thesisType) {
- data = (item.type ? item.type : item.thesisType);
+ data = item.getField("type");
+ if(!data) {
+ data = item.getField("thesisType");
}
dontEscape = false;
} else if(name == "group") {
@@ -1302,7 +1364,7 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
var child = element.children[i];
var string = this._getFieldValue(child.name, child, item,
- format, typeName);
+ format, typeName, bibCitElement);
if(string) {
childData.push(string);
}
@@ -1313,13 +1375,10 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
} else if(name == "text") {
data = this._getTerm(element["term-name"]);
dontEscape = false;
- } else if(name == "isbn") {
- if(item.ISBN) {
- data = this._formatLocator(null, element, item.ISBN, format);
- }
- } else if(name == "doi") {
- if(item.DOI) {
- data = this._formatLocator(null, element, item.DOI, format);
+ } else if(name == "isbn" || name == "doi") {
+ var field = item.getField(name.toUpperCase());
+ if(field) {
+ data = this._formatLocator(null, element, field, format);
}
} else if(name == "number") {
data = this._csl.number;
@@ -1338,14 +1397,14 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
var inheritElement;
if(CSL._inherit[substituteElement.name] && CSL._inherit[name]
&& CSL._inherit[substituteElement.name] == CSL._inherit[name]) {
- // if both substituteElement and the parent element inheirt from
+ // if both substituteElement and the parent element inherit from
// the same base element, apply styles here
inheritElement = element;
} else {
// search for elements with the same serialization
- if(typeName != undefined && this._serializations[typeName]
- && this._serializations[typeName][serialization]) {
- inheritElement = this._serializations[typeName][serialization];
+ if(typeName != undefined && bibCitElement._serializations[typeName]
+ && bibCitElement._serializations[typeName][serialization]) {
+ inheritElement = bibCitElement._serializations[typeName][serialization];
} else {
// otherwise, use defaults
inheritElement = this._getFieldDefaults(substituteElement.name);
@@ -1366,7 +1425,8 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
// get field value
data = this._getFieldValue(substituteElement.name,
- substituteElement, item, format);
+ substituteElement, item, format,
+ bibCitElement);
// return field value, if there is one; otherwise, keep processing
// the data
if(data) {
@@ -1376,4 +1436,73 @@ CSL.prototype._getFieldValue = function(name, element, item, format, typeName) {
}
return "";
-}
-\ No newline at end of file
+}
+
+/*
+ * THE FOLLOWING CODE IS SCHOLAR-SPECIFIC
+ * gets a list of possible CSL types, in order of preference, for an item
+ */
+ CSL._optionalTypeMappings = {
+ journalArticle:"article-journal",
+ magazineArticle:"article-magazine",
+ newspaperArticle:"article-newspaper",
+ thesis:"thesis",
+ letter:"personal communication",
+ manuscript:"manuscript",
+ interview:"interview",
+ film:"motion picture",
+ artwork:"graphic",
+ website:"webpage"
+};
+// TODO: check with Elena/APA/MLA on this
+CSL._fallbackTypeMappings = {
+ book:"book",
+ bookSection:"chapter",
+ journalArticle:"article",
+ magazineArticle:"article",
+ newspaperArticle:"article",
+ thesis:"book",
+ letter:"article",
+ manuscript:"book",
+ interview:"book",
+ film:"book",
+ artwork:"book",
+ website:"article"
+};
+
+CSL.prototype._getTypeFromItem = function(item) {
+ var scholarType = Scholar.ItemTypes.getName(item.getType());
+
+ // get type
+ Scholar.debug("CSL: parsing item of Scholar type "+scholarType);
+ return [CSL._optionalTypeMappings[scholarType], CSL._fallbackTypeMappings[scholarType]];
+}
+
+CSL.prototype._separateItemCreators = function(item) {
+ var authors = new Array();
+ var editors = new Array();
+ var translators = new Array();
+
+ var authorID = Scholar.CreatorTypes.getID("author");
+ var editorID = Scholar.CreatorTypes.getID("editor");
+ var translatorID = Scholar.CreatorTypes.getID("translator");
+
+ var creators = item.getCreators();
+ for(var j in creators) {
+ var creator = creators[j];
+
+ if(creator.creatorTypeID == editorID) {
+ editors.push(creator);
+ } else if(creator.creatorTypeID == translatorID) {
+ translators.push(creator);
+ } else if(creator.creatorTypeID == authorID) {
+ // TODO: do we just ignore contributors?
+ authors.push(creator);
+ }
+ }
+
+ return [authors, editors, translators];
+}
+/*
+ * END SCHOLAR-SPECIFIC CODE
+ */
+\ No newline at end of file
diff --git a/chrome/chromeFiles/content/scholar/xpcom/integration.js b/chrome/chromeFiles/content/scholar/xpcom/integration.js
@@ -24,22 +24,13 @@ Scholar.Integration = new function() {
}
/*
- * registers a SOAP method
- */
- function registerSOAP(name, callback) {
- _registeredSOAP[name] = callback;
- }
-
- /*
* handles an HTTP request
*/
function handleHeader(header) {
// get first line of request (all we care about for now)
- var spaceIndex = header.indexOf(" ");
- var method = header.substr(0, spaceIndex);
- var uri = header.substr(spaceIndex+1);
+ var method = header.substr(0, header.indexOf(" "));
- if(!method || !uri) {
+ if(!method) {
return _generateResponse("400 Bad Request");
}
@@ -60,7 +51,7 @@ Scholar.Integration = new function() {
* handles a SOAP envelope
*/
function handleEnvelope(envelope) {
- Scholar.debug("Got SOAP envelope");
+ Scholar.debug("Integration: got SOAP envelope");
Scholar.debug(envelope);
envelope = envelope.replace(_XMLRe, "");
@@ -68,11 +59,13 @@ Scholar.Integration = new function() {
var xml = new XML(envelope);
var request = xml.env::Body.children()[0];
if(request.namespace() != this.ns) {
- Scholar.debug("SOAP method not supported: invalid namespace");
+ Scholar.debug("Integration: SOAP method not supported: invalid namespace");
} else {
var name = request.localName();
if(Scholar.Integration.SOAP[name]) {
if(request.input.length()) {
+ // split apart passed parameters (same colon-escaped format
+ // as we pass)
var input = request.input.toString();
var vars = new Array();
vars[0] = "";
@@ -80,7 +73,6 @@ Scholar.Integration = new function() {
colonIndex = input.indexOf(":");
while(colonIndex != -1) {
- Scholar.debug(input);
if(input[colonIndex+1] == ":") { // escaped
vars[i] += input.substr(0, colonIndex+1);
input = input.substr(colonIndex+2);
@@ -128,7 +120,7 @@ Scholar.Integration = new function() {
return _generateResponse("200 OK", 'text/xml; charset="utf-8"',
responseEnvelope.toXMLString());
} else {
- Scholar.debug("SOAP method not supported");
+ Scholar.debug("Integration: SOAP method not supported");
}
}
}
@@ -304,7 +296,9 @@ Scholar.Integration.SOAP = new function() {
this.getCitation = getCitation;
this.getBibliography = getBibliography;
- function getCitation(parameters) {
+ function getCitation(vars) {
+ // get items
+
var myWindow = Components.classes["@mozilla.org/appshell/appShellService;1"]
.getService(Components.interfaces.nsIAppShellService)
.hiddenDOMWindow;
@@ -313,28 +307,47 @@ Scholar.Integration.SOAP = new function() {
myWindow.openDialog('chrome://scholar/content/selectItemsDialog.xul','',
'chrome,popup,modal,centerscreen,titlebar=no',io);
- if(io.dataOut && io.dataOut[0]) {
- var item = Scholar.Items.get(io.dataOut[0]);
- var creator = item.getCreator(0);
- return [io.dataOut[0], "{{"+creator.lastName+"}}"]
+ if(io.dataOut) { // cancel was not pressed
+ var selectedItemIDs = io.dataOut;
+ var selectedItems = Scholar.Items.get(selectedItemIDs);
+
+ var style = vars[0];
+ if(vars[1]) { // some items already exist in the document
+ var itemString = vars[1]; // underscore-delimited string
+
+ var newItemIndex = parseInt(vars[2]); // index at which the
+ // item belongs in
+ // itemString
+
+ // splice in the new item ID
+ if(newItemIndex == -1) { // at beginning
+ var items = selectedItems.concat(Scholar.Items.get(itemString.split("_")));
+ } else { // at newItemIndex
+ var items = Scholar.Items.get(itemString.substr(0, newItemIndex).split("_")).
+ concat(selectedItems);
+
+ if(newItemIndex != itemString.length) { // not at the end
+ items = items.concat(Scholar.Items.get(itemString.substr(newItemIndex+1).split("_")))
+ }
+ }
+ } else { // this is the first item and the only item to worry
+ // about
+ var items = selectedItems;
+ }
+
+ var citation = Scholar.Cite.getCitation(style, selectedItems, items, "Integration");
+
+ return [selectedItemIDs.join("_"), citation];
}
}
function getBibliography(vars) {
// get items
- var itemIDs = vars[1].split(",");
+ var itemIDs = vars[1].split("_");
var items = Scholar.Items.get(itemIDs);
return Scholar.Cite.getBibliography(vars[0], items, "Integration");
}
}
-/*Scholar.Integration.registerURL("/", "text/plain", function(vars) {
- return "Hi there! The HTTP server is working!";
-});
-Scholar.Integration.registerURL("/getBibliography", "text/html",
- function(vars) { return Scholar.Integration.Cite.getBibliography(vars) });
-Scholar.Integration.registerURL("/getCitation", "text/html",
- function(vars) { return Scholar.Integration.Cite.getCitation(vars) });*/
-
Scholar.Integration.init();
\ No newline at end of file
diff --git a/chrome/chromeFiles/locale/en-US/scholar/locales.xml b/chrome/chromeFiles/locale/en-US/scholar/locales.xml
@@ -7,6 +7,7 @@
<term name="forthcoming">forthcoming</term>
<term name="references">References</term>
<term name="and">and</term>
+ <term name="et-al">et al.</term>
<term-set name="locators">
<term name="page">
<single>page</single>
diff --git a/scrapers.sql b/scrapers.sql
@@ -1,4 +1,4 @@
--- 61
+-- 62
-- Set the following timestamp to the most recent scraper update date
REPLACE INTO "version" VALUES ('repository', STRFTIME('%s', '2006-08-15 15:42:00'));
@@ -5983,8 +5983,8 @@ REPLACE INTO "csl" VALUES('http://purl.org/net/xbiblio/csl/styles/apa.csl', '200
<et-al min-authors="6" use-first="1" position="subsequent"/>
<layout>
<item>
- <author form="short" suffix=", "/>
- <date>
+ <author form="short"/>
+ <date prefix=", ">
<year/>
</date>
<locator prefix=": "/>