/* * Scholar.Cite: a class for creating bibliographies from within Scholar * this class handles pulling the CSL file and item data out of the database, * while Scholar.CSL, below, handles the actual generation of the bibliography */ Scholar.Cite = new function() { this.getBibliography = getBibliography; this.getStyles = getStyles; function getStyles() { // get styles var sql = "SELECT cslID, title FROM csl ORDER BY title"; var styles = Scholar.DB.query(sql); // convert to associative array var stylesObject = new Object(); for each(var style in styles) { stylesObject[style.cslID] = style.title; } return stylesObject; } function getBibliography(cslID, items) { // get style var sql = "SELECT csl FROM csl WHERE cslID = ?"; var style = Scholar.DB.valueQuery(sql, [cslID]); // get item arrays var itemArrays = new Array(); for(var i in items) { itemArrays.push(items[i].toArray()); } // create a Scholar.CSL instance var CSL = new Scholar.CSL(style); // return bibliography return CSL.createBibliography(itemArrays); } } /* * Scholar.CSL: a class for creating bibliographies from CSL files * this is abstracted as a separate class for the benefit of anyone who doesn't * want to use the Scholar data model, but does want to use CSL in JavaScript */ /* * constructor */ Scholar.CSL = function(csl) { default xml namespace = Scholar.CSL.ns; this._csl = new XML(csl); // load basic options this._parseOptions(); } Scholar.CSL._loc = { and:"and", etAl:"et al", pSingle:"p.", pMultiple:"pp.", editorVerb:"Edited By", editorNounSingle:"Ed.", editorNounMultiple:"Eds.", translatorVerb:"Translated By", translatorNounSingle:"Trans.", translatorNounMultiple:"Trans.", months:["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"], monthsAbbreviated:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"], pagesShortSingle:"p", pagesShortMultiple:"pp", pagesLongSingle:"page", pagesLongMultiple:"pages" } Scholar.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 Scholar.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" }; Scholar.CSL.ns = "http://purl.org/net/xbiblio/csl"; /* * create a bibliography * (items is expected to be an array of items) */ Scholar.CSL.prototype.createBibliography = function(items) { // sort by sort order if(this._opt.sortOrder == "author-date") { items.sort(function(a, b) { // first make sure both have creators at the first index if(!a.creators[0] && b.creators[0]) { return 1; } else if(!b.creators[0] && a.creators[0]) { return -1; } // now, either both have creators or neither do if(a.creators[0]) { // sort by last names if(b.creators[0].lastName > a.creators[0].lastName) { return 1; } else if(b.creators[0].lastName < a.creators[0].lastName) { return -1; } // sort by first name if(b.creators[0].firstName > a.creators[0].firstName) { return 1; } else if(b.creators[0].firstName < a.creators[0].firstName) { return -1; } } // now, sort by date var date1 = (a.date ? a.date : a.year); var date2 = (b.date ? b.date : b.year); if(date2 > date1) { return 1; } else if(date1 > date2) { return -1; } // finally, give up; they're the same return 0; }); } // process items var output = ""; for(var i in items) { var item = items[i]; if(item.itemType == "note") { // skip notes continue; } // determine mapping if(Scholar.CSL._optionalTypeMappings[item.itemType] && this._opt.referenceTypes[Scholar.CSL._optionalTypeMappings[item.itemType]]) { if(this._opt.referenceTypes[Scholar.CSL._optionalTypeMappings[item.itemType]] === true) { // exists but not yet processed this._parseReferenceType(Scholar.CSL._optionalTypeMappings[item.itemType]); } var reftype = this._opt.referenceTypes[Scholar.CSL._optionalTypeMappings[item.itemType]]; } else { if(this._opt.referenceTypes[Scholar.CSL._fallbackTypeMappings[item.itemType]] === true) { this._parseReferenceType(Scholar.CSL._fallbackTypeMappings[item.itemType]); } var reftype = this._opt.referenceTypes[Scholar.CSL._fallbackTypeMappings[item.itemType]]; } output += "
"+this._processElements(reftype, item)+"
\n"; } return output; } /* * process an item */ Scholar.CSL.prototype._processElements = function(reftype, item) { var output = ""; // separate item into authors, editors, translators var authors = new Array(); var editors = new Array(); var translators = new Array(); for(var j in item.creators) { if(item.creators[j].creatorType == "editor") { editors.push(item.creators[j]); } else if(item.creators[j].creatorType == "translator") { translators.push(item.creators[j]); } else { authors.push(item.creators[j]); } } if(item.date) { // specific date var date = this._processDate(item.date); } else { // no real date, but might salvage a year var date = new Object(); if(item.year) { date.year = item.year; } } for(var i in reftype) { var element = reftype[i]; var data = ""; if(element.name == "author") { if(authors.length) { data = this._processCreators("author", authors); } else if(element.alternate) { // no authors; use alternate if // it exists if(element.alternate == "editor") { data = this._processCreators("editor", editors); editors = new Array(); } else if(element.alternate == "title") { data = item.title; item.title = undefined; } else if(element.alternate == "container-title") { if(item.publication) { data = item.publication; item.publication = undefined; } } } } else if(element.name == "editor") { data = this._processCreators("editor", editors); } else if(element.name == "translator") { data = this._processCreators("translator", translators); } else if(element.name == "year") { data = date.year; } else if(element.name == "month-day") { data = date.month+" "+date.day; } else if(element.name == "date") { data = this._formatDate(date); } else if(element.name == "volume") { data = item.volume; } else if(element.name == "issue") { data = item.number; } else if(element.name == "pages") { if(item.pages) { if(this._opt.locators.label) { if(item.pages.indexOf(",") != -1 || item.pages.indexOf("-") != -1) { var label = this._opt.locators.label[1]; } else { var label = this._opt.locators.label[0]; } if(this._opt.locators.positionBefore) { data += label; } } data += item.pages; if(this._opt.locators.label && !this._opt.locators.positionBefore) { data += label; } } } else if(element.name == "title") { if(!element.type) { // standard title data = item.title; } else if(element.type == "container" && item.publication) { data = item.publication; } else if(element.type == "series") { data = item.series; } } else if(element.name == "publisher") { if(item.publisher) { if(item.place) { if(this._opt.publishers.publisherFirst) { data = item.publisher+this._opt.publishers.separator+item.place; } else { data = item.place+this._opt.publishers.separator+item.publisher; } } else { data = item.publisher; } } } else if(element.name == "access") { var dateAccessed = ""; if(item.dateAccessed) { var dateAccessed = this._formatDate(this._processDate(item.dateAccessed)); } if(this._opt.access.dateFirst) { data = (dateAccessed ? dateAccessed : ""); } else { data = (item.url ? item.url : ""); } if(dateAccessed && item.url) { data += this._opt.access.separator; } if(this._opt.access.dateFirst) { data += item.url; } else { data += dateAccessed; } } else if(element.name == "group") { data = this._processElements(element.elements, item); } else { data = element.name; } style = ""; var cssAttributes = ["font-family", "font-style", "font-variant", "font-weight", "text-transform"]; for(var j in cssAttributes) { if(element[cssAttributes[j]] && element[cssAttributes[j]].indexOf('"') == -1) { style += cssAttributes[j]+":"+element[cssAttributes[j]]; } } if(data) { var data = data.toString(); // add prefix if(element.prefix) { output += element.prefix; } if(style) { output += ''; } output += data; if(style) { output += ''; } if(element.suffix) { // suffix for this element only output += element.suffix; } else if(element.name != "group" && this._opt.suffix && data.substr(data.length-this._opt.suffix.length) != this._opt.suffix) { // global suffix if no suffix for this element output += this._opt.suffix; } } } return output; } /* * process creator objects; if someone had a creator model that handled * non-Western names better than ours, this would be the function to change */ Scholar.CSL.prototype._processCreators = function(type, creators) { var maxCreators = creators.length; if(!maxCreators) return; 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; useEtAl = true; } // parse authors into strings var authorStrings = []; var firstName, lastName; for(var i=0; i