commit d22ccf2219463c95e6e94cfacf7f3a7b62808b21 parent 21a2188caa9951f34a0fe73a31a882c20aac6668 Author: Simon Kornblith <simon@simonster.com> Date: Mon, 25 Oct 2010 00:58:47 +0000 Zotero.Utilities changes: - Zotero.Utilities is now a singleton - Zotero.Utilities.HTTP is now Zotero.HTTP - Zotero.Utilities.md5 and Zotero.Utilities.Base64 are now located under Zotero.Utilities.Internal - Zotero.Utilities.AutoComplete has been eliminated This needs testing to make sure there is no associated breakage. Diffstat:
41 files changed, 1335 insertions(+), 1359 deletions(-)
diff --git a/chrome/content/zotero/bindings/itembox.xml b/chrome/content/zotero/bindings/itembox.xml @@ -272,7 +272,7 @@ var doi = this.item.getField('DOI'); if (doi && typeof val == 'String') { // Pull out DOI, in case there's a prefix - doi = Zotero.Utilities.prototype.cleanDOI(doi);; + doi = Zotero.Utilities.cleanDOI(doi);; if (doi) { spec = "http://dx.doi.org/" + encodeURIComponent(doi); } @@ -456,7 +456,7 @@ } else if (fieldName == 'DOI' && val && typeof val == 'string') { // Pull out DOI, in case there's a prefix - var doi = Zotero.Utilities.prototype.cleanDOI(val); + var doi = Zotero.Utilities.cleanDOI(val); if (doi) { doi = "http://dx.doi.org/" + encodeURIComponent(doi); label.setAttribute("isButton", true); @@ -1466,10 +1466,16 @@ <parameter name="textbox"/> <body> <![CDATA[ - var comment = Zotero.Utilities.AutoComplete.getResultComment(textbox); - if (!comment) + var comment = false; + var controller = textbox.controller; + + for (var i=0; i<controller.matchCount; i++) { - return; + if (controller.getValueAt(i) == textbox.value) + { + comment = controller.getCommentAt(i); + break; + } } var [creatorID, numFields] = comment.split('-'); @@ -1941,8 +1947,7 @@ var newVal = val.toLowerCase(); break; case 'title': - var utils = new Zotero.Utilities(); - var newVal = utils.capitalizeTitle(val.toLowerCase(), true); + var newVal = Zotero.Utilities.capitalizeTitle(val.toLowerCase(), true); break; default: throw ("Invalid transform mode '" + mode + "' in zoteroitembox.textTransform()"); diff --git a/chrome/content/zotero/bindings/styled-textbox.xml b/chrome/content/zotero/bindings/styled-textbox.xml @@ -223,11 +223,11 @@ output = output.replace(entry[0], entry[1], "g"); } - output = Zotero.Utilities.prototype.unescapeHTML( + output = Zotero.Utilities.unescapeHTML( output.replace(" ", " ", "g")) .replace(highcharRe, function(aChar) { return "\\uc0\\u"+aChar.charCodeAt(0).toString()+"{}" }); - output = Zotero.Utilities.prototype.trim(output); + output = Zotero.Utilities.trim(output); } return output; diff --git a/chrome/content/zotero/bindings/tagselector.xml b/chrome/content/zotero/bindings/tagselector.xml @@ -105,7 +105,7 @@ <parameter name="skipRefresh"/> <body> <![CDATA[ - if (!Zotero.Utilities.prototype.isEmpty(val)) { + if (!Zotero.Utilities.isEmpty(val)) { this._hasFilter = true; this._filter = val; } @@ -126,7 +126,7 @@ <property name="scope" onget="return this._scope"> <setter> <![CDATA[ - if (!Zotero.Utilities.prototype.isEmpty(val)) { + if (!Zotero.Utilities.isEmpty(val)) { this._hasScope = true; this._scope = val; } diff --git a/chrome/content/zotero/integration/addCitationDialog.js b/chrome/content/zotero/integration/addCitationDialog.js @@ -568,7 +568,7 @@ var Zotero_Citation_Dialog = new function () { } Zotero.debug("verified not custom"); - if(Zotero.Utilities.prototype.trim(citation) == "") { + if(Zotero.Utilities.trim(citation) == "") { var promptService = Components.classes["@mozilla.org/embedcomp/prompt-service;1"] .getService(Components.interfaces.nsIPromptService); var insert = promptService.confirm(window, diff --git a/chrome/content/zotero/lookup.js b/chrome/content/zotero/lookup.js @@ -30,7 +30,7 @@ const Zotero_Lookup = new function () { document.getElementById("accept-button").disabled = true; var identifier = document.getElementById("lookup-textbox").value; - var doi = Zotero.Utilities.prototype.cleanDOI(identifier); + var doi = Zotero.Utilities.cleanDOI(identifier); if(doi) { var item = {itemType:"journalArticle", DOI:doi}; } else { diff --git a/chrome/content/zotero/overlay.js b/chrome/content/zotero/overlay.js @@ -971,7 +971,7 @@ var ZoteroPane = new function() function clearTagSelection() { - if (!Zotero.Utilities.prototype.isEmpty(this.getTagSelection())) { + if (!Zotero.Utilities.isEmpty(this.getTagSelection())) { var tagSelector = document.getElementById('zotero-tag-selector'); tagSelector.clearAll(); } @@ -1547,7 +1547,7 @@ var ZoteroPane = new function() return; } - var title = Zotero.Utilities.prototype.trim(newTitle.value); + var title = Zotero.Utilities.trim(newTitle.value); if (!title) { return; @@ -1599,7 +1599,7 @@ var ZoteroPane = new function() return; } - var name = Zotero.Utilities.prototype.trim(newName.value); + var name = Zotero.Utilities.trim(newName.value); if (!name) { return; @@ -2492,7 +2492,7 @@ var ZoteroPane = new function() var doi = item.getField('DOI'); if (doi) { // Pull out DOI, in case there's a prefix - doi = Zotero.Utilities.prototype.cleanDOI(doi); + doi = Zotero.Utilities.cleanDOI(doi); if (doi) { uri = "http://dx.doi.org/" + encodeURIComponent(doi); } @@ -2720,12 +2720,12 @@ var ZoteroPane = new function() if (!text) { text = ''; } - text = Zotero.Utilities.prototype.trim(text); + text = Zotero.Utilities.trim(text); if (text) { text = '<blockquote' + (citeURI ? ' cite="' + citeURI + '"' : '') - + '>' + Zotero.Utilities.prototype.text2html(text) + "</blockquote>"; + + '>' + Zotero.Utilities.text2html(text) + "</blockquote>"; } var item = new Zotero.Item('note'); @@ -2769,7 +2769,7 @@ var ZoteroPane = new function() return false; } - text = Zotero.Utilities.prototype.trim(text); + text = Zotero.Utilities.trim(text); if (!text.length) { return false; @@ -2777,7 +2777,7 @@ var ZoteroPane = new function() text = '<blockquote' + (citeURI ? ' cite="' + citeURI + '"' : '') - + '>' + Zotero.Utilities.prototype.text2html(text) + "</blockquote>"; + + '>' + Zotero.Utilities.text2html(text) + "</blockquote>"; var items = this.getSelectedItems(); if (this.itemsView.selection.count == 1 && items[0] && items[0].isNote()) { @@ -3042,7 +3042,7 @@ var ZoteroPane = new function() Zotero.debug(e); } - Zotero.Utilities.HTTP.processDocuments([url], processor, done, exception); + Zotero.HTTP.processDocuments([url], processor, done, exception); } // Otherwise create placeholder item, attach attachment, and update from that else { diff --git a/chrome/content/zotero/preferences/preferences.js b/chrome/content/zotero/preferences/preferences.js @@ -847,14 +847,13 @@ function updatePDFToolsStatus() { // If we haven't already generated the required and documentation messages if (!converterIsRegistered && !requiredLabel.hasChildNodes()) { - var utils = new Zotero.Utilities(); // Xpdf link var str = Zotero.getString('zotero.preferences.search.pdf.toolsRequired', [Zotero.Fulltext.pdfConverterName, Zotero.Fulltext.pdfInfoName, '<a href="' + Zotero.Fulltext.pdfToolsURL + '">' + Zotero.Fulltext.pdfToolsName + '</a>']); - var parts = utils.parseMarkup(str); + var parts = Zotero.Utilities.parseMarkup(str); for (var i=0; i<parts.length; i++) { var part = parts[i]; if (part.type == 'text') { @@ -883,7 +882,7 @@ function updatePDFToolsStatus() { + Zotero.getString('zotero.preferences.search.pdf.documentationLink') + '</a>'; var str = Zotero.getString('zotero.preferences.search.pdf.advancedUsers', link); - var parts = utils.parseMarkup(str); + var parts = Zotero.Utilities.parseMarkup(str); for (var i=0; i<parts.length; i++) { var part = parts[i]; @@ -953,7 +952,7 @@ function checkPDFToolsDownloadVersion() { + Zotero.platform.replace(' ', '-') + '.latest'; // Find latest version for this platform - var sent = Zotero.Utilities.HTTP.doGet(url, function (xmlhttp) { + var sent = Zotero.HTTP.doGet(url, function (xmlhttp) { try { if (xmlhttp.status == 200) { var converterIsRegistered = Zotero.Fulltext.pdfConverterIsRegistered(); @@ -1082,7 +1081,7 @@ function installPDFTools(installVersions) { } // Find latest version for this platform - var sent = Zotero.Utilities.HTTP.doHead(url, function (xmlhttp) { + var sent = Zotero.HTTP.doHead(url, function (xmlhttp) { try { if (xmlhttp.status == 200) { // If doing both and on converter, chain pdfinfo @@ -1431,7 +1430,7 @@ Zotero_Preferences.Debug_Output = { + " (gzipped from " + oldLen + " bytes; " + savings + "% savings)"); - if (Zotero.Utilities.HTTP.browserIsOffline()) { + if (Zotero.HTTP.browserIsOffline()) { ps.alert( null, Zotero.getString( diff --git a/chrome/content/zotero/recognizePDF.js b/chrome/content/zotero/recognizePDF.js @@ -303,7 +303,7 @@ Zotero_RecognizePDF.Recognizer.prototype.recognize = function(file, libraryID, c // look for DOI var allText = lines.join("\n"); Zotero.debug(allText); - var m = Zotero.Utilities.prototype.cleanDOI(allText); + var m = Zotero.Utilities.cleanDOI(allText); if(m) { this._DOI = m[0]; } diff --git a/chrome/content/zotero/rtfScan.js b/chrome/content/zotero/rtfScan.js @@ -334,7 +334,7 @@ var Zotero_RTFScan = new function() { // make sure first name matches, if it exists if(creator.length > lowerLast.length) { - var firstName = Zotero.Utilities.prototype.trim(creator.substr(0, creator.length-lowerLast.length)); + var firstName = Zotero.Utilities.trim(creator.substr(0, creator.length-lowerLast.length)); if(firstName.length) { // check to see whether the first name is all initials const initialRe = /^(?:[A-Z]\.? ?)+$/; diff --git a/chrome/content/zotero/xpcom/collectionTreeView.js b/chrome/content/zotero/xpcom/collectionTreeView.js @@ -1445,7 +1445,7 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient) /* if (type == 'item') { - if (!Zotero.Utilities.prototype.isEmpty(changedCreators)) { + if (!Zotero.Utilities.isEmpty(changedCreators)) { io.dataIn.changedCreators = changedCreators; } } diff --git a/chrome/content/zotero/xpcom/commons.js b/chrome/content/zotero/xpcom/commons.js @@ -165,8 +165,8 @@ Zotero.Commons = new function() { Zotero.debug("IA BUCKETS"); Zotero.debug(IABuckets); - var addBuckets = Zotero.Utilities.prototype.arrayDiff(IABuckets, currentBuckets); - var removeBuckets = Zotero.Utilities.prototype.arrayDiff(currentBuckets, IABuckets); + var addBuckets = Zotero.Utilities.arrayDiff(IABuckets, currentBuckets); + var removeBuckets = Zotero.Utilities.arrayDiff(currentBuckets, IABuckets); Zotero.debug("ADD"); Zotero.debug(addBuckets); @@ -397,7 +397,7 @@ Zotero.Commons = new function() { this.slugify = function (input) { - var slug = Zotero.Utilities.prototype.trim(input) + var slug = Zotero.Utilities.trim(input) .toLowerCase() .replace(/[^a-z0-9 ._-]/g, "") //.replace(/ /g, "_"); @@ -458,7 +458,7 @@ Zotero.Commons.Bucket.prototype.exists = function (callback, maxTries, tries) { var self = this; - Zotero.Utilities.HTTP.doHead(this.uri, function (xmlhttp) { + Zotero.HTTP.doHead(this.uri, function (xmlhttp) { switch (xmlhttp.status) { case 200: callback(1); @@ -511,7 +511,7 @@ Zotero.Commons.Bucket.prototype.getItems = function (callback) { var progressWin = null; var progressWinIcon = 'chrome://zotero/skin/treeitem-attachment-pdf.png'; - var req = Zotero.Utilities.HTTP.doGet(uri, function (xmlhttp) { + var req = Zotero.HTTP.doGet(uri, function (xmlhttp) { if (xmlhttp.status != 200) { Zotero.debug(xmlhttp.status); Zotero.debug(xmlhttp.responseText); @@ -620,7 +620,7 @@ Zotero.Commons.Bucket.prototype.getItems = function (callback) { var rdfURI = self.downloadURI + '/' + zip.rdf; - Zotero.Utilities.HTTP.doGet(rdfURI, function (xmlhttp) { + Zotero.HTTP.doGet(rdfURI, function (xmlhttp) { // If RDF not available, skip item if (xmlhttp.status != 200) { Zotero.debug("RDF not found at " + xmlhttp.channel.originalURI.spec); @@ -1049,7 +1049,7 @@ Zotero.Commons.Bucket.prototype.updateMetadata = function(action, item, callback var self = this; // get previous metadata. multiple language support difficult via IA s3. - Zotero.Utilities.HTTP.doGet(resource, function (xmlhttp) { + Zotero.HTTP.doGet(resource, function (xmlhttp) { if (xmlhttp.status == 404 || (xmlhttp.status == 200 && !xmlhttp.responseXML)) { Zotero.Commons.error("Error updating bucket metadata"); return; diff --git a/chrome/content/zotero/xpcom/connector.js b/chrome/content/zotero/xpcom/connector.js @@ -39,7 +39,7 @@ Zotero.Connector = new function() { * initializes a very rudimentary web server */ this.init = function() { - if (Zotero.Utilities.HTTP.browserIsOffline()) { + if (Zotero.HTTP.browserIsOffline()) { Zotero.debug('Browser is offline -- not initializing connector HTTP server'); _registerOnlineObserver(); return; diff --git a/chrome/content/zotero/xpcom/data/collection.js b/chrome/content/zotero/xpcom/data/collection.js @@ -113,7 +113,7 @@ Zotero.Collection.prototype._set = function (field, val) { return; case 'name': - val = Zotero.Utilities.prototype.trim(val); + val = Zotero.Utilities.trim(val); break; } @@ -843,16 +843,16 @@ Zotero.Collection.prototype.diff = function (collection, includeMatches, ignoreO var numDiffs = Zotero.Collections.diff(thisData, otherData, diff, includeMatches); // For the moment, just compare children and increase numDiffs if any differences - var d1 = Zotero.Utilities.prototype.arrayDiff( + var d1 = Zotero.Utilities.arrayDiff( thisData.childCollections, otherData.childCollections ); - var d2 = Zotero.Utilities.prototype.arrayDiff( + var d2 = Zotero.Utilities.arrayDiff( otherData.childCollections, thisData.childCollections ); - var d3 = Zotero.Utilities.prototype.arrayDiff( + var d3 = Zotero.Utilities.arrayDiff( thisData.childItems, otherData.childItems ); - var d4 = Zotero.Utilities.prototype.arrayDiff( + var d4 = Zotero.Utilities.arrayDiff( otherData.childItems, thisData.childItems ); numDiffs += d1.length + d2.length; diff --git a/chrome/content/zotero/xpcom/data/creator.js b/chrome/content/zotero/xpcom/data/creator.js @@ -104,7 +104,7 @@ Zotero.Creator.prototype._set = function (field, val) { case 'lastName': case 'shortName': if (val) { - val = Zotero.Utilities.prototype.trim(val); + val = Zotero.Utilities.trim(val); } else { val = ''; diff --git a/chrome/content/zotero/xpcom/data/creators.js b/chrome/content/zotero/xpcom/data/creators.js @@ -332,8 +332,8 @@ Zotero.Creators = new function() { for each(var field in Zotero.Creators.fields) { hashFields.push(fields[field]); } - var ZU = new Zotero.Utilities; - return ZU.md5(hashFields.join('_')); + + return Zotero.Utilities.Internal.md5(hashFields.join('_')); } diff --git a/chrome/content/zotero/xpcom/data/item.js b/chrome/content/zotero/xpcom/data/item.js @@ -631,7 +631,7 @@ Zotero.Item.prototype.inCollection = function(collectionID) { */ Zotero.Item.prototype.setField = function(field, value, loadIn) { if (typeof value == 'string') { - value = Zotero.Utilities.prototype.trim(value); + value = Zotero.Utilities.trim(value); } this._disabledCheck(); @@ -1155,8 +1155,6 @@ Zotero.Item.prototype.save = function() { lastPos++; } - var ZU = new Zotero.Utilities; - Zotero.DB.beginTransaction(); var isNew = !this.id || !this.exists(); @@ -2339,7 +2337,7 @@ Zotero.Item.prototype.getNote = function() { // Convert non-HTML notes on-the-fly if (note) { if (!note.substr(0, 36).match(/^<div class="zotero-note znv[0-9]+">/)) { - note = Zotero.Utilities.prototype.htmlSpecialChars(note); + note = Zotero.Utilities.htmlSpecialChars(note); note = '<div class="zotero-note znv1"><p>' + note.replace(/\n/g, '</p><p>') .replace(/\t/g, ' ') @@ -2376,7 +2374,7 @@ Zotero.Item.prototype.setNote = function(text) { throw ("text must be a string in Zotero.Item.setNote() (was " + typeof text + ")"); } - text = Zotero.Utilities.prototype.trim(text); + text = Zotero.Utilities.trim(text); var oldText = this.getNote(); if (text == oldText) { @@ -3083,7 +3081,7 @@ Zotero.Item.prototype.__defineGetter__('attachmentHash', function () { return undefined; } - return Zotero.Utilities.prototype.md5(file); + return Zotero.Utilities.Internal.md5(file); }); @@ -3113,7 +3111,7 @@ Zotero.Item.prototype.__defineGetter__('attachmentText', function () { // TODO: remove post-Fx3.0 if (!str.trim) { - return Zotero.Utilities.prototype.trim(str); + return Zotero.Utilities.trim(str); } return str.trim(); @@ -3161,7 +3159,7 @@ Zotero.Item.prototype.__defineGetter__('attachmentText', function () { else if (mimeType == 'text/html') { str = Zotero.File.getContents(file); - str = Zotero.Utilities.prototype.unescapeHTML(str); + str = Zotero.Utilities.unescapeHTML(str); } else if (mimeType == 'text/plain') { @@ -3174,7 +3172,7 @@ Zotero.Item.prototype.__defineGetter__('attachmentText', function () { // TODO: remove post-Fx3.0 if (!str.trim) { - return Zotero.Utilities.prototype.trim(str); + return Zotero.Utilities.trim(str); } return str.trim(); @@ -3274,7 +3272,7 @@ Zotero.Item.prototype.addTag = function(name, type) { throw ('Cannot add tag to unsaved item in Item.addTag()'); } - name = Zotero.Utilities.prototype.trim(name); + name = Zotero.Utilities.trim(name); if (!name) { Zotero.debug('Not saving empty tag in Item.addTag()', 2); @@ -3426,7 +3424,7 @@ Zotero.Item.prototype.replaceTag = function(oldTagID, newTag) { throw ('Cannot replace tag on unsaved item'); } - newTag = Zotero.Utilities.prototype.trim(newTag); + newTag = Zotero.Utilities.trim(newTag); if (!newTag) { Zotero.debug('Not replacing with empty tag', 2); diff --git a/chrome/content/zotero/xpcom/data/notes.js b/chrome/content/zotero/xpcom/data/notes.js @@ -33,8 +33,8 @@ Zotero.Notes = new function() { * Return first line (or first MAX_LENGTH characters) of note content **/ function noteToTitle(text) { - text = Zotero.Utilities.prototype.trim(text); - text = Zotero.Utilities.prototype.unescapeHTML(text); + text = Zotero.Utilities.trim(text); + text = Zotero.Utilities.unescapeHTML(text); var max = this.MAX_TITLE_LENGTH; diff --git a/chrome/content/zotero/xpcom/data/tag.js b/chrome/content/zotero/xpcom/data/tag.js @@ -96,7 +96,7 @@ Zotero.Tag.prototype._set = function (field, val) { return; case 'name': - val = Zotero.Utilities.prototype.trim(val); + val = Zotero.Utilities.trim(val); break; } @@ -372,8 +372,8 @@ Zotero.Tag.prototype.save = function (full) { var sql = "SELECT itemID FROM itemTags WHERE tagID=?"; var dbItemIDs = Zotero.DB.columnQuery(sql, tagID); if (dbItemIDs) { - removed = Zotero.Utilities.prototype.arrayDiff(dbItemIDs, currentIDs); - newids = Zotero.Utilities.prototype.arrayDiff(currentIDs, dbItemIDs); + removed = Zotero.Utilities.arrayDiff(dbItemIDs, currentIDs); + newids = Zotero.Utilities.arrayDiff(currentIDs, dbItemIDs); } else { newids = currentIDs; @@ -381,10 +381,10 @@ Zotero.Tag.prototype.save = function (full) { } else { if (this._previousData.linkedItems) { - removed = Zotero.Utilities.prototype.arrayDiff( + removed = Zotero.Utilities.arrayDiff( this._previousData.linkedItems, currentIDs ); - newids = Zotero.Utilities.prototype.arrayDiff( + newids = Zotero.Utilities.arrayDiff( currentIDs, this._previousData.linkedItems ); } @@ -483,10 +483,10 @@ Zotero.Tag.prototype.diff = function (tag, includeMatches, ignoreOnlyDateModifie var numDiffs = Zotero.Tags.diff(thisData, otherData, diff, includeMatches); // For the moment, just compare linked items and increase numDiffs if any differences - var d1 = Zotero.Utilities.prototype.arrayDiff( + var d1 = Zotero.Utilities.arrayDiff( otherData.linkedItems, thisData.linkedItems ); - var d2 = Zotero.Utilities.prototype.arrayDiff( + var d2 = Zotero.Utilities.arrayDiff( thisData.linkedItems, otherData.linkedItems ); numDiffs += d1.length + d2.length; diff --git a/chrome/content/zotero/xpcom/data/tags.js b/chrome/content/zotero/xpcom/data/tags.js @@ -77,7 +77,7 @@ Zotero.Tags = new function() { * Returns the tagID matching given tag and type */ function getID(name, type, libraryID) { - name = Zotero.Utilities.prototype.trim(name); + name = Zotero.Utilities.trim(name); var lcname = name.toLowerCase(); if (!libraryID) { @@ -119,7 +119,7 @@ Zotero.Tags = new function() { * Returns all tagIDs for this tag (of all types) */ function getIDs(name, libraryID) { - name = Zotero.Utilities.prototype.trim(name); + name = Zotero.Utilities.trim(name); var sql = "SELECT tagID FROM tags WHERE name=? AND libraryID"; var params = [name]; if (libraryID) { @@ -137,7 +137,7 @@ Zotero.Tags = new function() { * Returns an array of tag types for tags matching given tag */ function getTypes(name, libraryID) { - name = Zotero.Utilities.prototype.trim(name); + name = Zotero.Utilities.trim(name); var sql = "SELECT type FROM tags WHERE name=? AND libraryID"; var params = [name]; if (libraryID) { @@ -285,7 +285,7 @@ Zotero.Tags = new function() { function rename(tagID, name) { Zotero.debug('Renaming tag', 4); - name = Zotero.Utilities.prototype.trim(name); + name = Zotero.Utilities.trim(name); Zotero.DB.beginTransaction(); diff --git a/chrome/content/zotero/xpcom/dataServer.js b/chrome/content/zotero/xpcom/dataServer.js @@ -46,7 +46,7 @@ Zotero.DataServer = new function () { return; } - if (Zotero.Utilities.HTTP.browserIsOffline()) { + if (Zotero.HTTP.browserIsOffline()) { Zotero.debug('Browser is offline -- not initializing data HTTP server'); _registerOnlineObserver() return; diff --git a/chrome/content/zotero/xpcom/db.js b/chrome/content/zotero/xpcom/db.js @@ -1190,11 +1190,10 @@ Zotero.DBConnection.prototype._getDBConnection = function () { // Levenshtein distance UDF var lev = { - ZU: new Zotero.Utilities, onFunctionCall: function (arg) { var a = arg.getUTF8String(0); var b = arg.getUTF8String(1); - return this.ZU.levenshtein(a, b); + return Zotero.Utilities.levenshtein(a, b); } }; this._connection.createFunction('levenshtein', 2, lev); @@ -1213,7 +1212,7 @@ Zotero.DBConnection.prototype._getDBConnection = function () { var rx = { onFunctionCall: function (arg) { var str = arg.getUTF8String(0); - return Zotero.Utilities.prototype.text2html(str, true); + return Zotero.Utilities.text2html(str, true); } }; this._connection.createFunction('text2html', 1, rx); diff --git a/chrome/content/zotero/xpcom/http.js b/chrome/content/zotero/xpcom/http.js @@ -0,0 +1,610 @@ +/** + * Functions for performing HTTP requests, both via XMLHTTPRequest and using a hidden browser + * @namespace + */ +Zotero.HTTP = new function() { + this.WebDAV = {}; + + + /** + * Send an HTTP GET request via XMLHTTPRequest + * + * @param {nsIURI|String} url URL to request + * @param {Function} onDone Callback to be executed upon request completion + * @param {String} responseCharset Character set to force on the response + * @return {Boolean} True if the request was sent, or false if the browser is offline + */ + this.doGet = function(url, onDone, responseCharset) { + if (url instanceof Components.interfaces.nsIURI) { + // Don't display password in console + var disp = url.clone(); + if (disp.password) { + disp.password = "********"; + } + Zotero.debug("HTTP GET " + disp.spec); + url = url.spec; + } + else { + Zotero.debug("HTTP GET " + url); + } + if (this.browserIsOffline()){ + return false; + } + + // Workaround for "Accept third-party cookies" being off in Firefox 3.0.1 + // https://www.zotero.org/trac/ticket/1070 + if (Zotero.isFx30) { + const Cc = Components.classes; + const Ci = Components.interfaces; + var ds = Cc["@mozilla.org/webshell;1"]. + createInstance(Components.interfaces.nsIDocShellTreeItem). + QueryInterface(Ci.nsIInterfaceRequestor); + ds.itemType = Ci.nsIDocShellTreeItem.typeContent; + var xmlhttp = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Ci.nsIXMLHttpRequest); + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open("GET", url, true); + xmlhttp.channel.loadGroup = ds.getInterface(Ci.nsILoadGroup); + xmlhttp.channel.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI; + } + else { + var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open('GET', url, true); + // Send cookie even if "Allow third-party cookies" is disabled (>=Fx3.6 only) + if (!Zotero.isFx35) { + var channel = xmlhttp.channel; + channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal); + channel.forceAllowThirdPartyCookie = true; + } + } + + // Don't cache GET requests + xmlhttp.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; + + /** @ignore */ + xmlhttp.onreadystatechange = function() { + _stateChange(xmlhttp, onDone, responseCharset); + }; + + xmlhttp.send(null); + + return xmlhttp; + } + + /** + * Send an HTTP POST request via XMLHTTPRequest + * + * @param {String} url URL to request + * @param {String} body Request body + * @param {Function} onDone Callback to be executed upon request completion + * @param {String} headers Request HTTP headers + * @param {String} responseCharset Character set to force on the response + * @return {Boolean} True if the request was sent, or false if the browser is offline + */ + this.doPost = function(url, body, onDone, headers, responseCharset) { + if (url instanceof Components.interfaces.nsIURI) { + // Don't display password in console + var disp = url.clone(); + if (disp.password) { + disp.password = "********"; + } + url = url.spec; + } + + var bodyStart = body.substr(0, 1024); + // Don't display sync password or session id in console + bodyStart = bodyStart.replace(/password=[^&]+/, 'password=********'); + bodyStart = bodyStart.replace(/sessionid=[^&]+/, 'sessionid=********'); + + Zotero.debug("HTTP POST " + + (body.length > 1024 ? + bodyStart + '... (' + body.length + ' chars)' : bodyStart) + + " to " + (disp ? disp.spec : url)); + + + if (this.browserIsOffline()){ + return false; + } + + // Workaround for "Accept third-party cookies" being off in Firefox 3.0.1 + // https://www.zotero.org/trac/ticket/1070 + if (Zotero.isFx30) { + const Cc = Components.classes; + const Ci = Components.interfaces; + var ds = Cc["@mozilla.org/webshell;1"]. + createInstance(Components.interfaces.nsIDocShellTreeItem). + QueryInterface(Ci.nsIInterfaceRequestor); + ds.itemType = Ci.nsIDocShellTreeItem.typeContent; + var xmlhttp = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Ci.nsIXMLHttpRequest); + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open("POST", url, true); + xmlhttp.channel.loadGroup = ds.getInterface(Ci.nsILoadGroup); + xmlhttp.channel.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI; + } + else { + var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open('POST', url, true); + // Send cookie even if "Allow third-party cookies" is disabled (>=Fx3.6 only) + if (!Zotero.isFx35) { + var channel = xmlhttp.channel; + channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal); + channel.forceAllowThirdPartyCookie = true; + } + } + + if (headers) { + if (typeof headers == 'string') { + var msg = "doPost() now takes a headers object rather than a requestContentType -- update your code"; + Zotero.debug(msg, 2); + Components.utils.reportError(msg); + headers = { + "Content-Type": headers + }; + } + } + else { + headers = {}; + } + + if (!headers["Content-Type"]) { + headers["Content-Type"] = "application/x-www-form-urlencoded"; + } + + for (var header in headers) { + xmlhttp.setRequestHeader(header, headers[header]); + } + + /** @ignore */ + xmlhttp.onreadystatechange = function(){ + _stateChange(xmlhttp, onDone, responseCharset); + }; + + xmlhttp.send(body); + + return xmlhttp; + } + + /** + * Send an HTTP HEAD request via XMLHTTPRequest + * + * @param {String} url URL to request + * @param {Function} onDone Callback to be executed upon request completion + * @param {Object} requestHeaders HTTP headers to include with request + * @return {Boolean} True if the request was sent, or false if the browser is offline + */ + this.doHead = function(url, onDone, requestHeaders) { + if (url instanceof Components.interfaces.nsIURI) { + // Don't display password in console + var disp = url.clone(); + if (disp.password) { + disp.password = "********"; + } + Zotero.debug("HTTP HEAD " + disp.spec); + url = url.spec; + } + else { + Zotero.debug("HTTP HEAD " + url); + + } + + if (this.browserIsOffline()){ + return false; + } + + // Workaround for "Accept third-party cookies" being off in Firefox 3.0.1 + // https://www.zotero.org/trac/ticket/1070 + if (Zotero.isFx30) { + const Cc = Components.classes; + const Ci = Components.interfaces; + var ds = Cc["@mozilla.org/webshell;1"]. + createInstance(Components.interfaces.nsIDocShellTreeItem). + QueryInterface(Ci.nsIInterfaceRequestor); + ds.itemType = Ci.nsIDocShellTreeItem.typeContent; + var xmlhttp = Cc["@mozilla.org/xmlextras/xmlhttprequest;1"]. + createInstance(Ci.nsIXMLHttpRequest); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open("HEAD", url, true); + xmlhttp.channel.loadGroup = ds.getInterface(Ci.nsILoadGroup); + xmlhttp.channel.loadFlags |= Ci.nsIChannel.LOAD_DOCUMENT_URI; + } + else { + var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open('HEAD', url, true); + // Send cookie even if "Allow third-party cookies" is disabled (>=Fx3.6 only) + if (!Zotero.isFx35) { + var channel = xmlhttp.channel; + channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal); + channel.forceAllowThirdPartyCookie = true; + } + } + + if (requestHeaders) { + for (var header in requestHeaders) { + xmlhttp.setRequestHeader(header, requestHeaders[header]); + } + } + + // Don't cache HEAD requests + xmlhttp.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; + + /** @ignore */ + xmlhttp.onreadystatechange = function(){ + _stateChange(xmlhttp, onDone); + }; + + xmlhttp.send(null); + + return xmlhttp; + } + + /** + * Send an HTTP OPTIONS request via XMLHTTPRequest + * + * @param {nsIURI} url + * @param {Function} onDone + * @return {XMLHTTPRequest} + */ + this.doOptions = function (uri, callback) { + // Don't display password in console + var disp = uri.clone(); + if (disp.password) { + disp.password = "********"; + } + Zotero.debug("HTTP OPTIONS for " + disp.spec); + + if (Zotero.HTTP.browserIsOffline()){ + return false; + } + + var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open('OPTIONS', uri.spec, true); + /** @ignore */ + xmlhttp.onreadystatechange = function() { + _stateChange(xmlhttp, callback); + }; + xmlhttp.send(null); + return xmlhttp; + } + + // + // WebDAV methods + // + + /** + * Send a WebDAV PROP* request via XMLHTTPRequest + * + * Returns false if browser is offline + * + * @param {String} method PROPFIND or PROPPATCH + * @param {nsIURI} uri + * @param {String} body XML string + * @param {Function} callback + * @param {Object} requestHeaders e.g. { Depth: 0 } + */ + this.WebDAV.doProp = function (method, uri, body, callback, requestHeaders) { + switch (method) { + case 'PROPFIND': + case 'PROPPATCH': + break; + + default: + throw ("Invalid method '" + method + + "' in Zotero.HTTP.doProp"); + } + + if (requestHeaders && requestHeaders.depth != undefined) { + var depth = requestHeaders.depth; + } + + // Don't display password in console + var disp = uri.clone(); + if (disp.password) { + disp.password = "********"; + } + + var bodyStart = body.substr(0, 1024); + Zotero.debug("HTTP " + method + " " + + (depth != undefined ? "(depth " + depth + ") " : "") + + (body.length > 1024 ? + bodyStart + "... (" + body.length + " chars)" : bodyStart) + + " to " + disp.spec); + + if (Zotero.HTTP.browserIsOffline()) { + Zotero.debug("Browser is offline", 2); + return false; + } + + var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open(method, uri.spec, true); + + if (requestHeaders) { + for (var header in requestHeaders) { + xmlhttp.setRequestHeader(header, requestHeaders[header]); + } + } + + xmlhttp.setRequestHeader("Content-Type", 'text/xml; charset="utf-8"'); + + xmlhttp.onreadystatechange = function() { + _stateChange(xmlhttp, callback); + }; + + xmlhttp.send(body); + return xmlhttp; + } + + + /** + * Send a WebDAV MKCOL request via XMLHTTPRequest + * + * @param {nsIURI} url + * @param {Function} onDone + * @return {XMLHTTPRequest} + */ + this.WebDAV.doMkCol = function (uri, callback) { + // Don't display password in console + var disp = uri.clone(); + if (disp.password) { + disp.password = "********"; + } + Zotero.debug("HTTP MKCOL " + disp.spec); + + if (Zotero.HTTP.browserIsOffline()) { + return false; + } + + var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open('MKCOL', uri.spec, true); + xmlhttp.onreadystatechange = function() { + _stateChange(xmlhttp, callback); + }; + xmlhttp.send(null); + return xmlhttp; + } + + + /** + * Send a WebDAV PUT request via XMLHTTPRequest + * + * @param {nsIURI} url + * @param {String} body String body to PUT + * @param {Function} onDone + * @return {XMLHTTPRequest} + */ + this.WebDAV.doPut = function (uri, body, callback) { + // Don't display password in console + var disp = uri.clone(); + if (disp.password) { + disp.password = "********"; + } + + var bodyStart = "'" + body.substr(0, 1024) + "'"; + Zotero.debug("HTTP PUT " + + (body.length > 1024 ? + bodyStart + "... (" + body.length + " chars)" : bodyStart) + + " to " + disp.spec); + + if (Zotero.HTTP.browserIsOffline()) { + return false; + } + + var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open("PUT", uri.spec, true); + // Some servers (e.g., Jungle Disk DAV) return a 200 response code + // with Content-Length: 0, which triggers a "no element found" error + // in Firefox, so we override to text + xmlhttp.overrideMimeType("text/plain"); + xmlhttp.onreadystatechange = function() { + _stateChange(xmlhttp, callback); + }; + xmlhttp.send(body); + return xmlhttp; + } + + + /** + * Send a WebDAV PUT request via XMLHTTPRequest + * + * @param {nsIURI} url + * @param {Function} onDone + * @return {XMLHTTPRequest} + */ + this.WebDAV.doDelete = function (uri, callback) { + // Don't display password in console + var disp = uri.clone(); + if (disp.password) { + disp.password = "********"; + } + + Zotero.debug("WebDAV DELETE to " + disp.spec); + + if (Zotero.HTTP.browserIsOffline()) { + return false; + } + + var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] + .createInstance(); + // Prevent certificate/authentication dialogs from popping up + xmlhttp.mozBackgroundRequest = true; + xmlhttp.open("DELETE", uri.spec, true); + // Firefox 3 throws a "no element found" error even with a + // 204 ("No Content") response, so we override to text + xmlhttp.overrideMimeType("text/plain"); + xmlhttp.onreadystatechange = function() { + _stateChange(xmlhttp, callback); + }; + xmlhttp.send(null); + return xmlhttp; + } + + + /** + * Get the Authorization header used by a channel + * + * As of Firefox 3.0.1 subsequent requests to higher-level directories + * seem not to authenticate properly and just return 401s, so this + * can be used to manually include the Authorization header in a request + * + * It can also be used to check whether a request was forced to + * use authentication + * + * @param {nsIChannel} channel + * @return {String|FALSE} Authorization header, or FALSE if none + */ + this.getChannelAuthorization = function (channel) { + try { + channel.QueryInterface(Components.interfaces.nsIHttpChannel); + var authHeader = channel.getRequestHeader("Authorization"); + return authHeader; + } + catch (e) { + Zotero.debug(e); + return false; + } + } + + + /** + * Checks if the browser is currently in "Offline" mode + * + * @type Boolean + */ + this.browserIsOffline = function() { + return Components.classes["@mozilla.org/network/io-service;1"] + .getService(Components.interfaces.nsIIOService).offline; + } + + /** + * Load one or more documents in a hidden browser + * + * @param {String|String[]} urls URL(s) of documents to load + * @param {Function} processor Callback to be executed for each document loaded + * @param {Function} done Callback to be executed after all documents have been loaded + * @param {Function} exception Callback to be executed if an exception occurs + */ + this.processDocuments = function(urls, processor, done, exception) { + /** + * Removes event listener for the load event and deletes the hidden browser + */ + var removeListeners = function() { + hiddenBrowser.removeEventListener(loadEvent, onLoad, true); + Zotero.Browser.deleteHiddenBrowser(hiddenBrowser); + } + + /** + * Loads the next page + * @inner + */ + var doLoad = function() { + if(urls.length) { + var url = urls.shift(); + try { + Zotero.debug("loading "+url); + hiddenBrowser.loadURI(url); + } catch(e) { + removeListeners(); + if(exception) { + exception(e); + return; + } else { + throw(e); + } + } + } else { + removeListeners(); + if(done) done(); + } + }; + + /** + * Callback to be executed when a page load completes + * @inner + */ + var onLoad = function() { + if(hiddenBrowser.contentDocument.location.href == "about:blank") return; + Zotero.debug(hiddenBrowser.contentDocument.location.href+" has been loaded"); + if(hiddenBrowser.contentDocument.location.href != prevUrl) { // Just in case it fires too many times + prevUrl = hiddenBrowser.contentDocument.location.href; + try { + processor(hiddenBrowser.contentDocument); + } catch(e) { + removeListeners(); + if(exception) { + exception(e); + return; + } else { + throw(e); + } + } + doLoad(); + } + }; + + if(typeof(urls) == "string") urls = [urls]; + + var prevUrl; + var loadEvent = Zotero.isFx2 ? "load" : "pageshow"; + + var hiddenBrowser = Zotero.Browser.createHiddenBrowser(); + hiddenBrowser.addEventListener(loadEvent, onLoad, true); + + doLoad(); + } + + /** + * Handler for XMLHttpRequest state change + * + * @param {nsIXMLHttpRequest} XMLHttpRequest whose state just changed + * @param {Function} [onDone] Callback for request completion + * @param {String} [responseCharset] Character set to force on the response + * @private + */ + function _stateChange(xmlhttp, callback, responseCharset, data) { + switch (xmlhttp.readyState){ + // Request not yet made + case 1: + break; + + case 2: + break; + + // Called multiple times while downloading in progress + case 3: + break; + + // Download complete + case 4: + if (callback) { + // Override the content charset + if (responseCharset) { + xmlhttp.channel.contentCharset = responseCharset; + } + callback(xmlhttp, data); + } + break; + } + } +} +\ No newline at end of file diff --git a/chrome/content/zotero/xpcom/ingester.js b/chrome/content/zotero/xpcom/ingester.js @@ -402,9 +402,9 @@ Zotero.OpenURL = new function() { } if(value.indexOf(",") !== -1) { - item.creators.push(Zotero.Utilities.prototype.cleanAuthor(value, type, true)); + item.creators.push(Zotero.Utilities.cleanAuthor(value, type, true)); } else { - item.creators.push(Zotero.Utilities.prototype.cleanAuthor(value, type, false)); + item.creators.push(Zotero.Utilities.cleanAuthor(value, type, false)); } } else if(key == "rft.aucorp") { complexAu.push({lastName:value, isInstitution:true}); diff --git a/chrome/content/zotero/xpcom/integration_compat.js b/chrome/content/zotero/xpcom/integration_compat.js @@ -43,7 +43,7 @@ Zotero.Integration.Compat = new function() { function init() { this.env = new Namespace("http://schemas.xmlsoap.org/soap/envelope/"); - if (Zotero.Utilities.HTTP.browserIsOffline()) { + if (Zotero.HTTP.browserIsOffline()) { Zotero.debug('Browser is offline -- not initializing integration HTTP server'); _registerOnlineObserver() return; diff --git a/chrome/content/zotero/xpcom/mime.js b/chrome/content/zotero/xpcom/mime.js @@ -281,7 +281,7 @@ Zotero.MIME = new function(){ this.getMIMETypeFromURL = function (url, callback) { - Zotero.Utilities.HTTP.doHead(url, function(xmlhttp) { + Zotero.HTTP.doHead(url, function(xmlhttp) { if (xmlhttp.status != 200 && xmlhttp.status != 204) { Zotero.debug("Attachment HEAD request returned with status code " + xmlhttp.status + " in Zotero.MIME.getMIMETypeFromURL()", 2); diff --git a/chrome/content/zotero/xpcom/progressWindow.js b/chrome/content/zotero/xpcom/progressWindow.js @@ -210,8 +210,7 @@ Zotero.ProgressWindow = function(_window){ newHB.setAttribute("class", "zotero-progress-item-hbox"); var newDescription = _progressWindow.document.createElement("description"); - var utils = new Zotero.Utilities(); - var parts = utils.parseMarkup(text); + var parts = Zotero.Utilities.parseMarkup(text); for each(var part in parts) { if (part.type == 'text') { var elem = _progressWindow.document.createTextNode(part.text); diff --git a/chrome/content/zotero/xpcom/quickCopy.js b/chrome/content/zotero/xpcom/quickCopy.js @@ -304,7 +304,7 @@ Zotero.QuickCopy = new function() { } } - var text = Zotero.Utilities.prototype.unescapeHTML(textXML.toXMLString()); + var text = Zotero.Utilities.unescapeHTML(textXML.toXMLString()); text = text.replace(new RegExp(ztab, "g"), " "); if (text.trim) { @@ -312,7 +312,7 @@ Zotero.QuickCopy = new function() { } // TODO: Remove once >=Fx3.5 else { - text = Zotero.Utilities.prototype.trim(text) + text = Zotero.Utilities.trim(text) } // diff --git a/chrome/content/zotero/xpcom/report.js b/chrome/content/zotero/xpcom/report.js @@ -36,7 +36,7 @@ Zotero.Report = new function() { var escapeXML = function (str) { str = str.replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\ud800-\udfff\ufffe\uffff]/g, '\u2B1A'); - return Zotero.Utilities.prototype.htmlSpecialChars(str); + return Zotero.Utilities.htmlSpecialChars(str); } @@ -224,7 +224,7 @@ Zotero.Report = new function() { continue; } - arr[i] = Zotero.Utilities.prototype.trim(arr[i] + ''); + arr[i] = Zotero.Utilities.trim(arr[i] + ''); // Skip empty fields if (!arr[i]) { diff --git a/chrome/content/zotero/xpcom/schema.js b/chrome/content/zotero/xpcom/schema.js @@ -227,7 +227,7 @@ Zotero.Schema = new function(){ return; } - str = Zotero.Utilities.prototype.trim(str); + str = Zotero.Utilities.trim(str); Zotero.debug(str); @@ -906,7 +906,7 @@ Zotero.Schema = new function(){ } } - var get = Zotero.Utilities.HTTP.doGet(url, function (xmlhttp) { + var get = Zotero.HTTP.doGet(url, function (xmlhttp) { var updated = _updateFromRepositoryCallback(xmlhttp, !!force); if (callback) { callback(xmlhttp, updated) @@ -1449,8 +1449,6 @@ Zotero.Schema = new function(){ Zotero.debug('Updating user data tables from version ' + fromVersion + ' to ' + toVersion); - var ZU = new Zotero.Utilities; - Zotero.DB.beginTransaction(); try { @@ -2458,7 +2456,7 @@ Zotero.Schema = new function(){ var rows = Zotero.DB.query("SELECT * FROM itemDataValues WHERE value REGEXP '(^\\s+|\\s+$)'"); if (rows) { for each(var row in rows) { - var trimmed = Zotero.Utilities.prototype.trim(row.value); + var trimmed = Zotero.Utilities.trim(row.value); var valueID = Zotero.DB.valueQuery("SELECT valueID FROM itemDataValues WHERE value=?", trimmed); if (valueID) { Zotero.DB.query("UPDATE OR REPLACE itemData SET valueID=? WHERE valueID=?", [valueID, row.valueID]); @@ -2494,7 +2492,7 @@ Zotero.Schema = new function(){ var rows = Zotero.DB.query("SELECT * FROM tags WHERE name REGEXP '(^\\s+|\\s+$)'"); if (rows) { for each(var row in rows) { - var trimmed = Zotero.Utilities.prototype.trim(row.name); + var trimmed = Zotero.Utilities.trim(row.name); var tagID = Zotero.DB.valueQuery("SELECT tagID FROM tags WHERE name=?", trimmed); if (tagID) { Zotero.DB.query("UPDATE OR REPLACE itemTags SET tagID=? WHERE tagID=?", [tagID, row.tagID]); diff --git a/chrome/content/zotero/xpcom/search.js b/chrome/content/zotero/xpcom/search.js @@ -114,7 +114,7 @@ Zotero.Search.prototype._set = function (field, val) { return; case 'name': - val = Zotero.Utilities.prototype.trim(val); + val = Zotero.Utilities.trim(val); break; } @@ -964,8 +964,6 @@ Zotero.Search.prototype._idsToTempTable = function (ids) { * Build the SQL query for the search */ Zotero.Search.prototype._buildQuery = function(){ - var utils = new Zotero.Utilities(); - var sql = 'SELECT itemID FROM items'; var sqlParams = []; // Separate ANY conditions for 'required' condition support @@ -1341,13 +1339,13 @@ Zotero.Search.prototype._buildQuery = function(){ // to '00' so that a search for just a year works // (and no year will just not find anything) var sqldate = dateparts.year ? - utils.lpad(dateparts.year, '0', 4) : '____'; + Zotero.Utilities.lpad(dateparts.year, '0', 4) : '____'; sqldate += '-' sqldate += dateparts.month || dateparts.month === 0 ? - utils.lpad(dateparts.month + 1, '0', 2) : alt; + Zotero.Utilities.lpad(dateparts.month + 1, '0', 2) : alt; sqldate += '-'; sqldate += dateparts.day ? - utils.lpad(dateparts.day, '0', 2) : alt; + Zotero.Utilities.lpad(dateparts.day, '0', 2) : alt; if (sqldate!='____-__-__'){ go = true; diff --git a/chrome/content/zotero/xpcom/storage.js b/chrome/content/zotero/xpcom/storage.js @@ -1067,7 +1067,7 @@ Zotero.Sync.Storage = new function () { var entryName = entries.getNext(); var b64re = /%ZB64$/; if (entryName.match(b64re)) { - var fileName = Zotero.Utilities.Base64.decode( + var fileName = Zotero.Utilities.Internal.Base64.decode( entryName.replace(b64re, '') ); } @@ -1410,7 +1410,7 @@ Zotero.Sync.Storage = new function () { //Zotero.debug("Adding file " + fileName); - fileName = Zotero.Utilities.Base64.encode(fileName) + "%ZB64"; + fileName = Zotero.Utilities.Internal.Base64.encode(fileName) + "%ZB64"; zipWriter.addEntryFile( fileName, Components.interfaces.nsIZipWriter.COMPRESSION_DEFAULT, @@ -1814,7 +1814,7 @@ Zotero.Sync.Storage.QueueManager = new function () { var kbRemaining = Zotero.getString( 'sync.storage.kbRemaining', - Zotero.Utilities.prototype.numberFormat(remaining / 1024, 0) + Zotero.Utilities.numberFormat(remaining / 1024, 0) ); var totalRequests = queue.totalRequests; var filesRemaining = Zotero.getString( diff --git a/chrome/content/zotero/xpcom/storage/webdav.js b/chrome/content/zotero/xpcom/storage/webdav.js @@ -224,7 +224,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype._getStorageModificationTime = funct var self = this; - Zotero.Utilities.HTTP.doGet(uri, function (req) { + Zotero.HTTP.doGet(uri, function (req) { self._checkResponse(req, self); var funcName = "Zotero.Sync.Storage.WebDAV_getStorageModificationTime()"; @@ -288,7 +288,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype._getStorageModificationTime = funct // Delete invalid .prop files if (invalid) { - var msg = "Invalid mod date '" + Zotero.Utilities.prototype.ellipsize(mtime, 20) + var msg = "Invalid mod date '" + Zotero.Utilities.ellipsize(mtime, 20) + "' for item " + Zotero.Items.getLibraryKeyHash(item); Zotero.debug(msg, 1); Components.utils.reportError(msg); @@ -320,7 +320,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype._setStorageModificationTime = funct <hash>{hash}</hash> </properties>; - Zotero.Utilities.HTTP.WebDAV.doPut(uri, prop.toXMLString(), function (req) { + Zotero.HTTP.WebDAV.doPut(uri, prop.toXMLString(), function (req) { switch (req.status) { case 200: case 201: @@ -707,7 +707,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.getLastSyncTime = function (callbac if (!this._cachedCredentials) { var self = this; - Zotero.Utilities.HTTP.doOptions(this.rootURI, function (req) { + Zotero.HTTP.doOptions(this.rootURI, function (req) { self._checkResponse(req, self); if (req.status != 200) { @@ -728,7 +728,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.getLastSyncTime = function (callbac var uri = this.rootURI; var successFileURI = uri.clone(); successFileURI.spec += "lastsync"; - Zotero.Utilities.HTTP.doHead(successFileURI, function (req) { + Zotero.HTTP.doHead(successFileURI, function (req) { var ts = undefined; try { if (req.responseText) { @@ -783,7 +783,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.setLastSyncTime = function (callbac var self = this; - Zotero.Utilities.HTTP.WebDAV.doPut(successFileURI, " ", function (req) { + Zotero.HTTP.WebDAV.doPut(successFileURI, " ", function (req) { Zotero.debug(req.responseText); Zotero.debug(req.status); @@ -865,7 +865,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.checkServer = function (callback) { var self = this; // Test whether URL is WebDAV-enabled - var request = Zotero.Utilities.HTTP.doOptions(uri, function (req) { + var request = Zotero.HTTP.doOptions(uri, function (req) { // Timeout if (req.status == 0) { self._checkResponse(req, self); @@ -904,12 +904,12 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.checkServer = function (callback) { // Get the Authorization header used in case we need to do a request // on the parent below - var channelAuthorization = Zotero.Utilities.HTTP.getChannelAuthorization(req.channel); + var channelAuthorization = Zotero.HTTP.getChannelAuthorization(req.channel); var headers = { Depth: 0 }; // Test whether Zotero directory exists - Zotero.Utilities.HTTP.WebDAV.doProp("PROPFIND", uri, xmlstr, function (req) { + Zotero.HTTP.WebDAV.doProp("PROPFIND", uri, xmlstr, function (req) { Zotero.debug(req.responseText); Zotero.debug(req.status); @@ -918,7 +918,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.checkServer = function (callback) { // Test if Zotero directory is writable var testFileURI = uri.clone(); testFileURI.spec += "zotero-test-file"; - Zotero.Utilities.HTTP.WebDAV.doPut(testFileURI, " ", function (req) { + Zotero.HTTP.WebDAV.doPut(testFileURI, " ", function (req) { Zotero.debug(req.responseText); Zotero.debug(req.status); @@ -926,7 +926,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.checkServer = function (callback) { case 200: case 201: case 204: - Zotero.Utilities.HTTP.doHead( + Zotero.HTTP.doHead( testFileURI, function (req) { Zotero.debug(req.responseText); @@ -935,7 +935,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.checkServer = function (callback) { switch (req.status) { case 200: // Delete test file - Zotero.Utilities.HTTP.WebDAV.doDelete( + Zotero.HTTP.WebDAV.doDelete( testFileURI, function (req) { Zotero.debug(req.responseText); @@ -1034,7 +1034,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.checkServer = function (callback) { // Zotero directory wasn't found, so see if at least // the parent directory exists - Zotero.Utilities.HTTP.WebDAV.doProp("PROPFIND", parentURI, xmlstr, + Zotero.HTTP.WebDAV.doProp("PROPFIND", parentURI, xmlstr, function (req) { Zotero.debug(req.responseText); Zotero.debug(req.status); @@ -1316,7 +1316,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.purgeOrphanedStorageFiles = functio var self = this; - Zotero.Utilities.HTTP.WebDAV.doProp("PROPFIND", uri, xmlstr, function (req) { + Zotero.HTTP.WebDAV.doProp("PROPFIND", uri, xmlstr, function (req) { Zotero.debug(req.responseText); var funcName = "Zotero.Sync.Storage.purgeOrphanedStorageFiles()"; @@ -1413,7 +1413,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype.purgeOrphanedStorageFiles = functio */ Zotero.Sync.Storage.Session.WebDAV.prototype._createServerDirectory = function (callback) { var uri = this.rootURI; - Zotero.Utilities.HTTP.WebDAV.doMkCol(uri, function (req) { + Zotero.HTTP.WebDAV.doMkCol(uri, function (req) { Zotero.debug(req.responseText); Zotero.debug(req.status); @@ -1536,7 +1536,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype._deleteStorageFiles = function (fil deleteURI.QueryInterface(Components.interfaces.nsIURL); deleteURI.fileName = files[i]; deleteURI.QueryInterface(Components.interfaces.nsIURI); - Zotero.Utilities.HTTP.WebDAV.doDelete(deleteURI, function (req) { + Zotero.HTTP.WebDAV.doDelete(deleteURI, function (req) { switch (req.status) { case 204: // IIS 5.1 and Sakai return 200 @@ -1586,7 +1586,7 @@ Zotero.Sync.Storage.Session.WebDAV.prototype._deleteStorageFiles = function (fil } // Delete property file - Zotero.Utilities.HTTP.WebDAV.doDelete(deletePropURI, function (req) { + Zotero.HTTP.WebDAV.doDelete(deletePropURI, function (req) { switch (req.status) { case 204: // IIS 5.1 and Sakai return 200 diff --git a/chrome/content/zotero/xpcom/storage/zfs.js b/chrome/content/zotero/xpcom/storage/zfs.js @@ -111,7 +111,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype._getStorageFileInfo = function (item, var self = this; - Zotero.Utilities.HTTP.doGet(uri, function (req) { + Zotero.HTTP.doGet(uri, function (req) { var funcName = "Zotero.Sync.Storage.Session.ZFS._getStorageFileInfo()"; if (req.status == 404) { @@ -467,7 +467,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype._getFileUploadParameters = function (i } var mtime = item.attachmentModificationTime; - var hash = Zotero.Utilities.prototype.md5(file); + var hash = Zotero.Utilities.Internal.md5(file); var body = "md5=" + hash + "&filename=" + encodeURIComponent(filename) + "&filesize=" + file.fileSize + "&mtime=" + mtime; @@ -477,7 +477,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype._getFileUploadParameters = function (i var self = this; - Zotero.Utilities.HTTP.doPost(uri, body, function (req) { + Zotero.HTTP.doPost(uri, body, function (req) { var funcName = "Zotero.Sync.Storage.Session.ZFS._getFileUploadParameters()"; if (req.status == 413) { @@ -571,7 +571,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype._getFileUploadParameters = function (i try { // Strip XML declaration and convert to E4X - var xml = new XML(Zotero.Utilities.prototype.trim(req.responseText.replace(/<\?xml.*\?>/, ''))); + var xml = new XML(Zotero.Utilities.trim(req.responseText.replace(/<\?xml.*\?>/, ''))); } catch (e) { self.onError("Invalid response retrieving file upload parameters"); @@ -748,7 +748,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype._onUploadComplete = function (httpRequ var self = this; // Register upload on server - Zotero.Utilities.HTTP.doPost(uri, body, function (req) { + Zotero.HTTP.doPost(uri, body, function (req) { if (req.status != 204) { var msg = "Unexpected file registration status " + req.status + " in Zotero.Sync.Storage._onUploadComplete()" @@ -832,7 +832,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype.getLastSyncTime = function (callback) var uri = this.rootURI; // TODO: move to root uri uri.spec += "?auth=1"; - Zotero.Utilities.HTTP.doGet(uri, function (req) { + Zotero.HTTP.doGet(uri, function (req) { if (req.status != 200) { var msg = "Unexpected status code " + req.status + " caching " + "authentication credentials in Zotero.Sync.Storage.Session.ZFS.getLastSyncTime()"; @@ -847,7 +847,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype.getLastSyncTime = function (callback) return; } - Zotero.Utilities.HTTP.doGet(successFileURI, function (req) { + Zotero.HTTP.doGet(successFileURI, function (req) { if (req.responseText) { Zotero.debug(req.responseText); } @@ -910,7 +910,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype.setLastSyncTime = function (callback, var self = this; - Zotero.Utilities.HTTP.doPost(successFileURI, "", function (req) { + Zotero.HTTP.doPost(successFileURI, "", function (req) { Zotero.debug(req.responseText); Zotero.debug(req.status); @@ -971,7 +971,7 @@ Zotero.Sync.Storage.Session.ZFS.prototype.purgeDeletedStorageFiles = function (c var self = this; - Zotero.Utilities.HTTP.doPost(uri, "", function (xmlhttp) { + Zotero.HTTP.doPost(uri, "", function (xmlhttp) { if (xmlhttp.status != 204) { if (callback) { callback(false); diff --git a/chrome/content/zotero/xpcom/style.js b/chrome/content/zotero/xpcom/style.js @@ -253,7 +253,7 @@ Zotero.Styles = new function() { if(source && !_styles[source]) { // need to fetch source if(source.substr(0, 7) == "http://" || source.substr(0, 8) == "https://") { - Zotero.Utilities.HTTP.doGet(source, function(xmlhttp) { + Zotero.HTTP.doGet(source, function(xmlhttp) { var success = false; var error = null; try { diff --git a/chrome/content/zotero/xpcom/sync.js b/chrome/content/zotero/xpcom/sync.js @@ -395,8 +395,6 @@ Zotero.Sync.EventListener = new function () { var isItem = Zotero.Sync.getObjectTypeName(objectTypeID) == 'item'; - var ZU = new Zotero.Utilities; - Zotero.DB.beginTransaction(); if (event == 'delete') { @@ -504,7 +502,7 @@ Zotero.Sync.Runner = new function () { this.sync = function (background) { _warning = null; - if (Zotero.Utilities.HTTP.browserIsOffline()){ + if (Zotero.HTTP.browserIsOffline()){ this.clearSyncTimeout(); // DEBUG: necessary? var msg = "Zotero cannot sync while Firefox is in offline mode."; var e = new Zotero.Error(msg, 0, { dialogButtonText: null }) @@ -1241,7 +1239,7 @@ Zotero.Sync.Server = new function () { Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.loggingIn')); - Zotero.Utilities.HTTP.doPost(url, body, function (xmlhttp) { + Zotero.HTTP.doPost(url, body, function (xmlhttp) { _checkResponse(xmlhttp); var response = xmlhttp.responseXML.childNodes[0]; @@ -1325,7 +1323,7 @@ Zotero.Sync.Server = new function () { Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.gettingUpdatedData')); - Zotero.Utilities.HTTP.doPost(url, body, function (xmlhttp) { + Zotero.HTTP.doPost(url, body, function (xmlhttp) { Zotero.debug(xmlhttp.responseText); _checkResponse(xmlhttp); @@ -1352,7 +1350,7 @@ Zotero.Sync.Server = new function () { _error(response.firstChild.firstChild.nodeValue); } - var xml = Zotero.Utilities.prototype.trim(xmlhttp.responseText.replace(/<\?xml.*\?>\s*/, '')); + var xml = Zotero.Utilities.trim(xmlhttp.responseText.replace(/<\?xml.*\?>\s*/, '')); // Strip XML declaration and convert to E4X xml = new XML(xml); @@ -1495,7 +1493,7 @@ Zotero.Sync.Server = new function () { var url = _serverURL + 'uploadstatus'; var body = _apiVersionComponent + '&' + Zotero.Sync.Server.sessionIDComponent; - Zotero.Utilities.HTTP.doPost(url, body, function (xmlhttp) { + Zotero.HTTP.doPost(url, body, function (xmlhttp) { uploadCallback(xmlhttp); }); break; @@ -1559,7 +1557,7 @@ Zotero.Sync.Server = new function () { + " (gzipped from " + oldLen + " bytes; " + savings + "% savings)"); - if (Zotero.Utilities.HTTP.browserIsOffline()) { + if (Zotero.HTTP.browserIsOffline()) { Zotero.debug('Browser is offline'); return false; } @@ -1609,7 +1607,7 @@ Zotero.Sync.Server = new function () { // Don't compress upload data else { - Zotero.Utilities.HTTP.doPost(url, body, uploadCallback); + Zotero.HTTP.doPost(url, body, uploadCallback); } } catch (e) { @@ -1637,7 +1635,7 @@ Zotero.Sync.Server = new function () { var body = _apiVersionComponent + '&' + Zotero.Sync.Server.sessionIDComponent; - Zotero.Utilities.HTTP.doPost(url, body, function (xmlhttp) { + Zotero.HTTP.doPost(url, body, function (xmlhttp) { if (_invalidSession(xmlhttp)) { Zotero.debug("Invalid session ID -- logging in"); _sessionID = false; @@ -1698,7 +1696,7 @@ Zotero.Sync.Server = new function () { _sessionID = null; - Zotero.Utilities.HTTP.doPost(url, body, function (xmlhttp) { + Zotero.HTTP.doPost(url, body, function (xmlhttp) { _checkResponse(xmlhttp); Zotero.debug(xmlhttp.responseText); @@ -2223,7 +2221,7 @@ Zotero.Sync.Server = new function () { if (extraInfo) { // Server errors will generally be HTML - extraInfo = Zotero.Utilities.prototype.unescapeHTML(extraInfo); + extraInfo = Zotero.Utilities.unescapeHTML(extraInfo); Components.utils.reportError(extraInfo); } @@ -2853,12 +2851,12 @@ Zotero.Sync.Server.Data = new function() { } } // Add - toAdd = Zotero.Utilities.prototype.arrayDiff(toAdd, existing); + toAdd = Zotero.Utilities.arrayDiff(toAdd, existing); var changed = toAdd.length > 0; existing = existing.concat(toAdd); var origLen = existing.length; // Remove - existing = Zotero.Utilities.prototype.arrayDiff(existing, toRemove); + existing = Zotero.Utilities.arrayDiff(existing, toRemove); changed = changed || origLen != existing.length; // Set if (changed) { @@ -3220,7 +3218,7 @@ Zotero.Sync.Server.Data = new function() { if (!itemIDs) { return false; } - var newItemIDs = Zotero.Utilities.prototype.arrayDiff(itemIDs, childItems); + var newItemIDs = Zotero.Utilities.arrayDiff(itemIDs, childItems); if (itemIDs.length == newItemIDs.length) { return false; } @@ -3462,7 +3460,7 @@ Zotero.Sync.Server.Data = new function() { }; if (type == 'item') { - if (!Zotero.Utilities.prototype.isEmpty(changedCreators)) { + if (!Zotero.Utilities.isEmpty(changedCreators)) { io.dataIn.changedCreators = changedCreators; } } diff --git a/chrome/content/zotero/xpcom/timeline.js b/chrome/content/zotero/xpcom/timeline.js @@ -29,8 +29,7 @@ Zotero.Timeline = new function () { this.generateXMLList = generateXMLList; function generateXMLDetails(items, dateType) { - var ZU = new Zotero.Utilities(); - var escapeXML = ZU.htmlSpecialChars; + var escapeXML = Zotero.Utilities.htmlSpecialChars; var content = '<data>\n'; for each(var arr in items) { diff --git a/chrome/content/zotero/xpcom/translate.js b/chrome/content/zotero/xpcom/translate.js @@ -959,7 +959,7 @@ Zotero.Translate.prototype._generateSandbox = function() { // add utilities this._sandbox.Zotero.Utilities = new Zotero.Utilities.Translate(this); - this._sandbox.Zotero.Utilities.HTTP = this._sandbox.Zotero.Utilities; + this._sandbox.Zotero.HTTP = this._sandbox.Zotero.Utilities; if(this.type == "export") { // add routines to retrieve items and collections @@ -1319,7 +1319,7 @@ Zotero.Translate.prototype._reportTranslationFailure = function(errorData) { "&lastUpdated=" + encodeURIComponent(this.translator[0].lastUpdated) + "&diagnostic=" + encodeURIComponent(Zotero.getSystemInfo()) + "&errorData=" + encodeURIComponent(errorData); - Zotero.Utilities.HTTP.doPost("http://www.zotero.org/repo/report", postBody); + Zotero.HTTP.doPost("http://www.zotero.org/repo/report", postBody); } } diff --git a/chrome/content/zotero/xpcom/utilities.js b/chrome/content/zotero/xpcom/utilities.js @@ -29,610 +29,484 @@ /** * @class Functions for text manipulation and other miscellaneous purposes */ -Zotero.Utilities = function () {} - -/** - * Cleans extraneous punctuation off a creator name and parse into first and last name - * - * @param {String} author Creator string - * @param {String} type Creator type string (e.g., "author" or "editor") - * @param {Boolean} useComma Whether the creator string is in inverted (Last, First) format - * @return {Object} firstName, lastName, and creatorType - */ -Zotero.Utilities.prototype.cleanAuthor = function(author, type, useComma) { - const allCapsRe = /^[A-Z\u0400-\u042f]+$/; - - if(typeof(author) != "string") { - throw "cleanAuthor: author must be a string"; - } - - author = author.replace(/^[\s\.\,\/\[\]\:]+/, ''); - author = author.replace(/[\s\,\/\[\]\:\.]+$/, ''); - author = author.replace(/ +/, ' '); - if(useComma) { - // Add spaces between periods - author = author.replace(/\.([^ ])/, ". $1"); +Zotero.Utilities = { + /** + * Cleans extraneous punctuation off a creator name and parse into first and last name + * + * @param {String} author Creator string + * @param {String} type Creator type string (e.g., "author" or "editor") + * @param {Boolean} useComma Whether the creator string is in inverted (Last, First) format + * @return {Object} firstName, lastName, and creatorType + */ + "cleanAuthor":function(author, type, useComma) { + const allCapsRe = /^[A-Z\u0400-\u042f]+$/; - var splitNames = author.split(/, ?/); - if(splitNames.length > 1) { - var lastName = splitNames[0]; - var firstName = splitNames[1]; + if(typeof(author) != "string") { + throw "cleanAuthor: author must be a string"; + } + + author = author.replace(/^[\s\.\,\/\[\]\:]+/, ''); + author = author.replace(/[\s\,\/\[\]\:\.]+$/, ''); + author = author.replace(/ +/, ' '); + if(useComma) { + // Add spaces between periods + author = author.replace(/\.([^ ])/, ". $1"); + + var splitNames = author.split(/, ?/); + if(splitNames.length > 1) { + var lastName = splitNames[0]; + var firstName = splitNames[1]; + } else { + var lastName = author; + } } else { - var lastName = author; + var spaceIndex = author.lastIndexOf(" "); + var lastName = author.substring(spaceIndex+1); + var firstName = author.substring(0, spaceIndex); } - } else { - var spaceIndex = author.lastIndexOf(" "); - var lastName = author.substring(spaceIndex+1); - var firstName = author.substring(0, spaceIndex); - } - - if(firstName && allCapsRe.test(firstName) && - firstName.length < 4 && - (firstName.length == 1 || lastName.toUpperCase() != lastName)) { - // first name is probably initials - var newFirstName = ""; - for(var i=0; i<firstName.length; i++) { - newFirstName += " "+firstName[i]+"."; + + if(firstName && allCapsRe.test(firstName) && + firstName.length < 4 && + (firstName.length == 1 || lastName.toUpperCase() != lastName)) { + // first name is probably initials + var newFirstName = ""; + for(var i=0; i<firstName.length; i++) { + newFirstName += " "+firstName[i]+"."; + } + firstName = newFirstName.substr(1); } - firstName = newFirstName.substr(1); - } - - return {firstName:firstName, lastName:lastName, creatorType:type}; -} - -/** - * Removes leading and trailing whitespace from a string - * @type String - */ -Zotero.Utilities.prototype.trim = function(/**String*/ s) { - if (typeof(s) != "string") { - Zotero.debug(s); - throw "trim: argument must be a string"; - } - - s = s.replace(/^\s+/, ""); - return s.replace(/\s+$/, ""); -} - -/** - * Cleans whitespace off a string and replaces multiple spaces with one - * @type String - */ -Zotero.Utilities.prototype.trimInternal = function(/**String*/ s) { - if (typeof(s) != "string") { - throw "trimInternal: argument must be a string"; - } - - s = s.replace(/[\xA0\r\n\s]+/g, " "); - return this.trim(s); -} - -/** - * Cleans whitespace off a string and replaces multiple spaces with one - * - * @deprecated Use trimInternal - * @see Zotero.Utilities#trimInternal - * @type String - */ -Zotero.Utilities.prototype.cleanString = function(/**String*/ s) { - Zotero.debug("cleanString() is deprecated; use trimInternal() instead", 2); - return this.trimInternal(s); -} - -/** - * Cleans any non-word non-parenthesis characters off the ends of a string - * @type String - */ -Zotero.Utilities.prototype.superCleanString = function(/**String*/ x) { - if(typeof(x) != "string") { - throw "superCleanString: argument must be a string"; - } - - var x = x.replace(/^[\x00-\x27\x29-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/, ""); - return x.replace(/[\x00-\x28\x2A-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+$/, ""); -} - -/** - * Eliminates HTML tags, replacing <br>s with newlines - * @type String - */ -Zotero.Utilities.prototype.cleanTags = function(/**String*/ x) { - if(typeof(x) != "string") { - throw "cleanTags: argument must be a string"; - } - - x = x.replace(/<br[^>]*>/gi, "\n"); - return x.replace(/<[^>]+>/g, ""); -} - - -Zotero.Utilities.prototype.cleanDOI = function(/**String**/ x) { - if(typeof(x) != "string") { - throw "cleanDOI: argument must be a string"; - } + + return {firstName:firstName, lastName:lastName, creatorType:type}; + }, - return x.match(/10\.[^\s\/]+\/[^\s]+/); -} + /** + * Removes leading and trailing whitespace from a string + * @type String + */ + "trim":function(/**String*/ s) { + if (typeof(s) != "string") { + Zotero.debug(s); + throw "trim: argument must be a string"; + } + + s = s.replace(/^\s+/, ""); + return s.replace(/\s+$/, ""); + }, + /** + * Cleans whitespace off a string and replaces multiple spaces with one + * @type String + */ + "trimInternal":function(/**String*/ s) { + if (typeof(s) != "string") { + throw "trimInternal: argument must be a string"; + } + + s = s.replace(/[\xA0\r\n\s]+/g, " "); + return this.trim(s); + }, -Zotero.Utilities.prototype.text2html = function (/**String**/ str, singleNewlineIsParagraph) { - str = Zotero.Utilities.prototype.htmlSpecialChars(str); + /** + * Cleans any non-word non-parenthesis characters off the ends of a string + * @type String + */ + "superCleanString":function(/**String*/ x) { + if(typeof(x) != "string") { + throw "superCleanString: argument must be a string"; + } + + var x = x.replace(/^[\x00-\x27\x29-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+/, ""); + return x.replace(/[\x00-\x28\x2A-\x2F\x3A-\x40\x5B-\x60\x7B-\x7F]+$/, ""); + }, - // \n => <p> - if (singleNewlineIsParagraph) { - str = '<p>' - + str.replace(/\n/g, '</p><p>') - .replace(/\t/g, ' ') - .replace(/ /g, ' ') - + '</p>'; - } - // \n\n => <p>, \n => <br/> - else { - str = Zotero.Utilities.prototype.htmlSpecialChars(str); - str = '<p>' - + str.replace(/\n\n/g, '</p><p>') - .replace(/\n/g, '<br/>') - .replace(/\t/g, ' ') - .replace(/ /g, ' ') - + '</p>'; - } - return str.replace(/<p>\s*<\/p>/g, '<p> </p>'); -} - + /** + * Eliminates HTML tags, replacing <br>s with newlines + * @type String + */ + "cleanTags":function(/**String*/ x) { + if(typeof(x) != "string") { + throw "cleanTags: argument must be a string"; + } + + x = x.replace(/<br[^>]*>/gi, "\n"); + return x.replace(/<[^>]+>/g, ""); + }, -/** - * Encode special XML/HTML characters<br/> - * <br/> - * Certain entities can be inserted manually:<br/> - * <pre> <ZOTEROBREAK/> => <br/> - * <ZOTEROHELLIP/> => &#8230;</pre> - * @type String - */ -Zotero.Utilities.prototype.htmlSpecialChars = function(/**String*/ str) { - if (typeof str != 'string') { - throw "Argument '" + str + "' must be a string in Zotero.Utilities.htmlSpecialChars()"; - } - - if (!str) { - return ''; - } - - var chars = ['&', '"',"'",'<','>']; - var entities = ['amp', 'quot', 'apos', 'lt', 'gt']; - - var newString = str; - for (var i = 0; i < chars.length; i++) { - var re = new RegExp(chars[i], 'g'); - newString = newString.replace(re, '&' + entities[i] + ';'); - } - - newString = newString.replace(/<ZOTERO([^\/]+)\/>/g, function (str, p1, offset, s) { - switch (p1) { - case 'BREAK': - return '<br/>'; - case 'HELLIP': - return '…'; - default: - return p1; + /** + * Strip info:doi prefix and any suffixes from a DOI + * @type String + */ + "cleanDOI":function(/**String**/ x) { + if(typeof(x) != "string") { + throw "cleanDOI: argument must be a string"; } - }); - - return newString; -} + + return x.match(/10\.[^\s\/]+\/[^\s]+/); + }, -/** - * Decodes HTML entities within a string, returning plain text - * @type String - */ -Zotero.Utilities.prototype.unescapeHTML = function(/**String*/ str) { - var nsISUHTML = Components.classes["@mozilla.org/feed-unescapehtml;1"] - .getService(Components.interfaces.nsIScriptableUnescapeHTML); - return nsISUHTML.unescape(str); -} + /** + * Convert plain text to HTML by replacing special characters and replacing newlines with BRs or + * P tags + * @param {String} str Plain text string + * @param {Boolean} singleNewlineIsParagraph Whether single newlines should be considered as + * paragraphs. If true, each newline is replaced with a P tag. If false, double newlines + * are replaced with P tags, while single newlines are replaced with BR tags. + * @type String + */ + "text2html":function (/**String**/ str, /**Boolean**/ singleNewlineIsParagraph) { + str = Zotero.Utilities.htmlSpecialChars(str); + + // \n => <p> + if (singleNewlineIsParagraph) { + str = '<p>' + + str.replace(/\n/g, '</p><p>') + .replace(/\t/g, ' ') + .replace(/ /g, ' ') + + '</p>'; + } + // \n\n => <p>, \n => <br/> + else { + str = Zotero.Utilities.htmlSpecialChars(str); + str = '<p>' + + str.replace(/\n\n/g, '</p><p>') + .replace(/\n/g, '<br/>') + .replace(/\t/g, ' ') + .replace(/ /g, ' ') + + '</p>'; + } + return str.replace(/<p>\s*<\/p>/g, '<p> </p>'); + }, + /** + * Encode special XML/HTML characters<br/> + * <br/> + * Certain entities can be inserted manually:<br/> + * <pre> <ZOTEROBREAK/> => <br/> + * <ZOTEROHELLIP/> => &#8230;</pre> + * @type String + */ + "htmlSpecialChars":function(/**String*/ str) { + if (typeof str != 'string') { + throw "Argument '" + str + "' must be a string in Zotero.Utilities.htmlSpecialChars()"; + } + + if (!str) { + return ''; + } + + var chars = ['&', '"',"'",'<','>']; + var entities = ['amp', 'quot', 'apos', 'lt', 'gt']; + + var newString = str; + for (var i = 0; i < chars.length; i++) { + var re = new RegExp(chars[i], 'g'); + newString = newString.replace(re, '&' + entities[i] + ';'); + } + + newString = newString.replace(/<ZOTERO([^\/]+)\/>/g, function (str, p1, offset, s) { + switch (p1) { + case 'BREAK': + return '<br/>'; + case 'HELLIP': + return '…'; + default: + return p1; + } + }); + + return newString; + }, -/** - * Wrap URLs and DOIs in <a href=""> links in plain text - * - * Ignore URLs preceded by '>', just in case there are already links - */ -Zotero.Utilities.prototype.autoLink = function (str) { - // "http://www.google.com." - // "http://www.google.com. " - // "<http://www.google.com>" (and other characters, with or without a space after) - str = str.replace(/([^>])(https?:\/\/[^\s]+)([\."'>:\]\)](\s|$))/g, '$1<a href="$2">$2</a>$3'); - // "http://www.google.com" - // "http://www.google.com " - str = str.replace(/([^">])(https?:\/\/[^\s]+)(\s|$)/g, '$1<a href="$2">$2</a>$3'); + /** + * Decodes HTML entities within a string, returning plain text + * @type String + */ + "unescapeHTML":function(/**String*/ str) { + var nsISUHTML = Components.classes["@mozilla.org/feed-unescapehtml;1"] + .getService(Components.interfaces.nsIScriptableUnescapeHTML); + return nsISUHTML.unescape(str); + }, - // DOI - str = str.replace(/(doi:[ ]*)(10\.[^\s]+[0-9a-zA-Z])/g, '$1<a href="http://dx.doi.org/$2">$2</a>'); - return str; -} - - -/** - * Parses a text string for HTML/XUL markup and returns an array of parts. Currently only finds - * HTML links (<a> tags) - * - * @return {Array} An array of objects with the following form:<br> - * <pre> { - * type: 'text'|'link', - * text: "text content", - * [ attributes: { key1: val [ , key2: val, ...] } - * }</pre> - */ -Zotero.Utilities.prototype.parseMarkup = function(/**String*/ str) { - var parts = []; - var splits = str.split(/(<a [^>]+>[^<]*<\/a>)/); + /** + * Wrap URLs and DOIs in <a href=""> links in plain text + * + * Ignore URLs preceded by '>', just in case there are already links + * @type String + */ + "autoLink":function (/**String**/ str) { + // "http://www.google.com." + // "http://www.google.com. " + // "<http://www.google.com>" (and other characters, with or without a space after) + str = str.replace(/([^>])(https?:\/\/[^\s]+)([\."'>:\]\)](\s|$))/g, '$1<a href="$2">$2</a>$3'); + // "http://www.google.com" + // "http://www.google.com " + str = str.replace(/([^">])(https?:\/\/[^\s]+)(\s|$)/g, '$1<a href="$2">$2</a>$3'); + + // DOI + str = str.replace(/(doi:[ ]*)(10\.[^\s]+[0-9a-zA-Z])/g, '$1<a href="http://dx.doi.org/$2">$2</a>'); + return str; + }, - for each(var split in splits) { - // Link - if (split.indexOf('<a ') == 0) { - var matches = split.match(/<a ([^>]+)>([^<]*)<\/a>/); - if (matches) { - // Attribute pairs - var attributes = {}; - var pairs = matches[1].match(/([^ =]+)="([^"]+")/g); - for each (var pair in pairs) { - var [key, val] = pair.split(/=/); - attributes[key] = val.substr(1, val.length - 2); + /** + * Parses a text string for HTML/XUL markup and returns an array of parts. Currently only finds + * HTML links (<a> tags) + * + * @return {Array} An array of objects with the following form:<br> + * <pre> { + * type: 'text'|'link', + * text: "text content", + * [ attributes: { key1: val [ , key2: val, ...] } + * }</pre> + */ + "parseMarkup":function(/**String*/ str) { + var parts = []; + var splits = str.split(/(<a [^>]+>[^<]*<\/a>)/); + + for each(var split in splits) { + // Link + if (split.indexOf('<a ') == 0) { + var matches = split.match(/<a ([^>]+)>([^<]*)<\/a>/); + if (matches) { + // Attribute pairs + var attributes = {}; + var pairs = matches[1].match(/([^ =]+)="([^"]+")/g); + for each (var pair in pairs) { + var [key, val] = pair.split(/=/); + attributes[key] = val.substr(1, val.length - 2); + } + + parts.push({ + type: 'link', + text: matches[2], + attributes: attributes + }); + continue; } - - parts.push({ - type: 'link', - text: matches[2], - attributes: attributes - }); - continue; } + + parts.push({ + type: 'text', + text: split + }); } - parts.push({ - type: 'text', - text: split - }); - } - - return parts; -} - -Zotero.Utilities.prototype.levenshtein = function (a, b) { - var aLen = a.length; - var bLen = b.length; - - var arr = new Array(aLen+1); - var i, j, cost; + return parts; + }, - for (i = 0; i <= aLen; i++) { - arr[i] = new Array(bLen); - arr[i][0] = i; - } - - for (j = 0; j <= bLen; j++) { - arr[0][j] = j; - } - - for (i = 1; i <= aLen; i++) { - for (j = 1; j <= bLen; j++) { - cost = (a[i-1] == b[j-1]) ? 0 : 1; - arr[i][j] = Math.min(arr[i-1][j] + 1, Math.min(arr[i][j-1] + 1, arr[i-1][j-1] + cost)); + /** + * Calculates the Levenshtein distance between two strings + * @type Number + */ + "levenshtein":function (/**String*/ a, /**String**/ b) { + var aLen = a.length; + var bLen = b.length; + + var arr = new Array(aLen+1); + var i, j, cost; + + for (i = 0; i <= aLen; i++) { + arr[i] = new Array(bLen); + arr[i][0] = i; } - } - - return arr[aLen][bLen]; -} - - -/** - * Test if an object is empty - * - * @param {Object} - */ -Zotero.Utilities.prototype.isEmpty = function (obj) { - for (var i in obj) { - return false; - } - return true; -} - - -/** - * Compares an array with another and returns an array with - * the values from array1 that don't exist in array2 - * - * @param {Array} array1 - * @param {Array} array2 - * @param {Boolean} useIndex If true, return an array containing just - * the index of array2's elements; - * otherwise return the values - */ -Zotero.Utilities.prototype.arrayDiff = function(array1, array2, useIndex) { - if (array1.constructor.name != 'Array') { - throw ("array1 is not an array in Zotero.Utilities.arrayDiff() (" + array1 + ")"); - } - if (array2.constructor.name != 'Array') { - throw ("array2 is not an array in Zotero.Utilities.arrayDiff() (" + array2 + ")"); - } - - var val, pos, vals = []; - for (var i=0; i<array1.length; i++) { - val = array1[i]; - pos = array2.indexOf(val); - if (pos == -1) { - vals.push(useIndex ? pos : val); + + for (j = 0; j <= bLen; j++) { + arr[0][j] = j; } - } - return vals; -} - - -/** - * Generate a random integer between min and max inclusive - * - * @param {Integer} min - * @param {Integer} max - * @return {Integer} - */ -Zotero.Utilities.prototype.rand = function (min, max) { - return Math.floor(Math.random() * (max - min + 1)) + min; -} - - -/* - * Adapted from http://developer.mozilla.org/en/docs/nsICryptoHash - * - * @param {String|nsIFile} strOrFile - * @param {Boolean} [base64=false] Return as base-64-encoded string rather than hex string - * @return {String} - */ -Zotero.Utilities.prototype.md5 = function(strOrFile, base64) { - if (typeof strOrFile == 'string') { - var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. - createInstance(Components.interfaces.nsIScriptableUnicodeConverter); - converter.charset = "UTF-8"; - var result = {}; - var data = converter.convertToByteArray(strOrFile, result); - var ch = Components.classes["@mozilla.org/security/hash;1"] - .createInstance(Components.interfaces.nsICryptoHash); - ch.init(ch.MD5); - ch.update(data, data.length); - } - else if (strOrFile instanceof Components.interfaces.nsIFile) { - // Otherwise throws (NS_ERROR_NOT_AVAILABLE) [nsICryptoHash.updateFromStream] - if (!strOrFile.fileSize) { - // MD5 for empty string - return "d41d8cd98f00b204e9800998ecf8427e"; + + for (i = 1; i <= aLen; i++) { + for (j = 1; j <= bLen; j++) { + cost = (a[i-1] == b[j-1]) ? 0 : 1; + arr[i][j] = Math.min(arr[i-1][j] + 1, Math.min(arr[i][j-1] + 1, arr[i-1][j-1] + cost)); + } } - var istream = Components.classes["@mozilla.org/network/file-input-stream;1"] - .createInstance(Components.interfaces.nsIFileInputStream); - // open for reading - istream.init(strOrFile, 0x01, 0444, 0); - var ch = Components.classes["@mozilla.org/security/hash;1"] - .createInstance(Components.interfaces.nsICryptoHash); - // we want to use the MD5 algorithm - ch.init(ch.MD5); - // this tells updateFromStream to read the entire file - const PR_UINT32_MAX = 0xffffffff; - ch.updateFromStream(istream, PR_UINT32_MAX); - } - - // pass false here to get binary data back - var hash = ch.finish(base64); - - if (istream) { - istream.close(); - } - - if (base64) { - return hash; - } - - /* - // This created 36-character hashes - - // return the two-digit hexadecimal code for a byte - function toHexString(charCode) { - return ("0" + charCode.toString(16)).slice(-2); - } - - // convert the binary hash data to a hex string. - return [toHexString(hash.charCodeAt(i)) for (i in hash)].join(""); - */ + return arr[aLen][bLen]; + }, - // From http://rcrowley.org/2007/11/15/md5-in-xulrunner-or-firefox-extensions/ - var ascii = []; - var ii = hash.length; - for (var i = 0; i < ii; ++i) { - var c = hash.charCodeAt(i); - var ones = c % 16; - var tens = c >> 4; - ascii.push(String.fromCharCode(tens + (tens > 9 ? 87 : 48)) + String.fromCharCode(ones + (ones > 9 ? 87 : 48))); - } - return ascii.join(''); -} - - -/** - * Parse a page range - * - * @param {String} Page range to parse - * @return {Integer[]} Start and end pages - */ -Zotero.Utilities.prototype.getPageRange = function(pages) { - const pageRangeRegexp = /^\s*([0-9]+) ?[-\u2013] ?([0-9]+)\s*$/ - - var pageNumbers; - var m = pageRangeRegexp.exec(pages); - if(m) { - // A page range - pageNumbers = [m[1], m[2]]; - } else { - // Assume start and end are the same - pageNumbers = [pages, pages]; - } - return pageNumbers; -} - -/** - * Pads a number or other string with a given string on the left - * - * @param {String} string String to pad - * @param {String} pad String to use as padding - * @length {Integer} length Length of new padded string - * @type String - */ -Zotero.Utilities.prototype.lpad = function(string, pad, length) { - string = string ? string + '' : ''; - while(string.length < length) { - string = pad + string; - } - return string; -} - - -/** - * Shorten and add an ellipsis to a string if necessary - * - * @param {String} str - * @param {Integer} len - * @param {Boolean} [countChars=false] - */ -Zotero.Utilities.prototype.ellipsize = function (str, len, countChars) { - if (!len) { - throw ("Length not specified in Zotero.Utilities.ellipsize()"); - } - if (str.length > len) { - return str.substr(0, len) + '...' + (countChars ? ' (' + str.length + ' chars)' : ''); - } - return str; -} - - -/** - * Port of PHP's number_format() - * - * MIT Licensed - * - * From http://kevin.vanzonneveld.net - * + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) - * + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) - * + bugfix by: Michael White (http://getsprink.com) - * + bugfix by: Benjamin Lupton - * + bugfix by: Allan Jensen (http://www.winternet.no) - * + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) - * + bugfix by: Howard Yeend - * * example 1: number_format(1234.5678, 2, '.', ''); - * * returns 1: 1234.57 - */ -Zotero.Utilities.prototype.numberFormat = function (number, decimals, dec_point, thousands_sep) { - var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; - var d = dec_point == undefined ? "." : dec_point; - var t = thousands_sep == undefined ? "," : thousands_sep, s = n < 0 ? "-" : ""; - var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0; - - return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); -} + /** + * Test if an object is empty + * + * @param {Object} obj + * @type Boolean + */ + "isEmpty":function (obj) { + for (var i in obj) { + return false; + } + return true; + }, + /** + * Compares an array with another and returns an array with + * the values from array1 that don't exist in array2 + * + * @param {Array} array1 + * @param {Array} array2 + * @param {Boolean} useIndex If true, return an array containing just + * the index of array2's elements; + * otherwise return the values + */ + "arrayDiff":function(array1, array2, useIndex) { + if (array1.constructor.name != 'Array') { + throw ("array1 is not an array in Zotero.Utilities.arrayDiff() (" + array1 + ")"); + } + if (array2.constructor.name != 'Array') { + throw ("array2 is not an array in Zotero.Utilities.arrayDiff() (" + array2 + ")"); + } + + var val, pos, vals = []; + for (var i=0; i<array1.length; i++) { + val = array1[i]; + pos = array2.indexOf(val); + if (pos == -1) { + vals.push(useIndex ? pos : val); + } + } + return vals; + }, -/** - * Tests if an item type exists - * - * @param {String} type Item type - * @type Boolean - */ -Zotero.Utilities.prototype.itemTypeExists = function(type) { - if(Zotero.ItemTypes.getID(type)) { - return true; - } else { - return false; - } -} + /** + * Generate a random integer between min and max inclusive + * + * @param {Integer} min + * @param {Integer} max + * @return {Integer} + */ + "rand":function (min, max) { + return Math.floor(Math.random() * (max - min + 1)) + min; + }, -/** - * Find valid creator types for a given item type - * - * @param {String} type Item type - * @return {String[]} Creator types - */ -Zotero.Utilities.prototype.getCreatorsForType = function(type) { - var types = Zotero.CreatorTypes.getTypesForItemType(Zotero.ItemTypes.getID(type)); - var cleanTypes = new Array(); - for each(var type in types) { - cleanTypes.push(type.name); - } - return cleanTypes; -} + /** + * Parse a page range + * + * @param {String} Page range to parse + * @return {Integer[]} Start and end pages + */ + "getPageRange":function(pages) { + const pageRangeRegexp = /^\s*([0-9]+) ?[-\u2013] ?([0-9]+)\s*$/ + + var pageNumbers; + var m = pageRangeRegexp.exec(pages); + if(m) { + // A page range + pageNumbers = [m[1], m[2]]; + } else { + // Assume start and end are the same + pageNumbers = [pages, pages]; + } + return pageNumbers; + }, -/** - * Gets a creator type name, localized to the current locale - * - * @param {String} type Creator type - * @param {String} Localized creator type - * @type Boolean - */ -Zotero.Utilities.prototype.getLocalizedCreatorType = function(type) { - try { - return Zotero.getString("creatorTypes."+type); - } catch(e) { - return false; - } -} + /** + * Pads a number or other string with a given string on the left + * + * @param {String} string String to pad + * @param {String} pad String to use as padding + * @length {Integer} length Length of new padded string + * @type String + */ + "lpad":function(string, pad, length) { + string = string ? string + '' : ''; + while(string.length < length) { + string = pad + string; + } + return string; + }, -/** - * Cleans a title, converting it to title case and replacing " :" with ":" - * - * @param {String} string - * @param {Boolean} force Forces title case conversion, even if the capitalizeTitles pref is off - * @type String - */ -Zotero.Utilities.prototype.capitalizeTitle = function(string, force) { - string = this.trimInternal(string); - if(Zotero.Prefs.get('capitalizeTitles') || force) { - // fix colons - string = string.replace(" : ", ": ", "g"); - string = Zotero.Text.titleCase(string.replace(/ : /g, ": ")); - } - return string; -} + /** + * Shorten and add an ellipsis to a string if necessary + * + * @param {String} str + * @param {Integer} len + * @param {Boolean} [countChars=false] + */ + "ellipsize":function (str, len, countChars) { + if (!len) { + throw ("Length not specified in Zotero.Utilities.ellipsize()"); + } + if (str.length > len) { + return str.substr(0, len) + '...' + (countChars ? ' (' + str.length + ' chars)' : ''); + } + return str; + }, + + /** + * Port of PHP's number_format() + * + * MIT Licensed + * + * From http://kevin.vanzonneveld.net + * + original by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) + * + improved by: Kevin van Zonneveld (http://kevin.vanzonneveld.net) + * + bugfix by: Michael White (http://getsprink.com) + * + bugfix by: Benjamin Lupton + * + bugfix by: Allan Jensen (http://www.winternet.no) + * + revised by: Jonas Raoni Soares Silva (http://www.jsfromhell.com) + * + bugfix by: Howard Yeend + * * example 1: number_format(1234.5678, 2, '.', ''); + * * returns 1: 1234.57 + */ + "numberFormat":function (number, decimals, dec_point, thousands_sep) { + var n = number, c = isNaN(decimals = Math.abs(decimals)) ? 2 : decimals; + var d = dec_point == undefined ? "." : dec_point; + var t = thousands_sep == undefined ? "," : thousands_sep, s = n < 0 ? "-" : ""; + var i = parseInt(n = Math.abs(+n || 0).toFixed(c)) + "", j = (j = i.length) > 3 ? j % 3 : 0; + + return s + (j ? i.substr(0, j) + t : "") + i.substr(j).replace(/(\d{3})(?=\d)/g, "$1" + t) + (c ? d + Math.abs(n - i).toFixed(c).slice(2) : ""); + }, + /** + * Cleans a title, converting it to title case and replacing " :" with ":" + * + * @param {String} string + * @param {Boolean} force Forces title case conversion, even if the capitalizeTitles pref is off + * @type String + */ + "capitalizeTitle":function(string, force) { + string = this.trimInternal(string); + string = string.replace(" : ", ": ", "g"); + if(Zotero.Prefs.get('capitalizeTitles') || force) { + // fix colons + string = Zotero.Text.titleCase(string); + } + return string; + }, -/** - * Run sets of data through multiple asynchronous callbacks - * - * Each callback is passed the current set and a callback to call when done - * - * @param {Object[]} sets Sets of data - * @param {Function[]} callbacks - * @param {Function} onDone Function to call when done - */ -Zotero.Utilities.prototype.processAsync = function (sets, callbacks, onDone) { - var currentSet; - var index = 0; - - var nextSet = function () { - if (!sets.length) { - onDone(); - return; + /** + * Run sets of data through multiple asynchronous callbacks + * + * Each callback is passed the current set and a callback to call when done + * + * @param {Object[]} sets Sets of data + * @param {Function[]} callbacks + * @param {Function} onDone Function to call when done + */ + "processAsync":function (sets, callbacks, onDone) { + var currentSet; + var index = 0; + + var nextSet = function () { + if (!sets.length) { + onDone(); + return; + } + index = 0; + currentSet = sets.shift(); + callbacks[0](currentSet, nextCallback); + }; + var nextCallback = function () { + index++; + callbacks[index](currentSet, nextCallback); + }; + + // Add a final callback to proceed to the next set + callbacks[callbacks.length] = function () { + nextSet(); } - index = 0; - currentSet = sets.shift(); - callbacks[0](currentSet, nextCallback); - }; - var nextCallback = function () { - index++; - callbacks[index](currentSet, nextCallback); - }; - - // Add a final callback to proceed to the next set - callbacks[callbacks.length] = function () { nextSet(); } - nextSet(); } - /** * @class All functions accessible from within Zotero.Utilities namespace inside sandboxed * translators @@ -645,14 +519,23 @@ Zotero.Utilities.prototype.processAsync = function (sets, callbacks, onDone) { * @borrows Zotero.Date.strToISO as this.strToISO * @borrows Zotero.OpenURL.createContextObject as this.createContextObject * @borrows Zotero.OpenURL.parseContextObject as this.parseContextObject - * @borrows Zotero.Utilities.HTTP.processDocuments as this.processDocuments - * @borrows Zotero.Utilities.HTTP.doPost as this.doPost + * @borrows Zotero.HTTP.processDocuments as this.processDocuments + * @borrows Zotero.HTTP.doPost as this.doPost * @param {Zotero.Translate} translate */ Zotero.Utilities.Translate = function(translate) { this.translate = translate; } +var tmp = function() {}; +tmp.prototype = Zotero.Utilities; +Zotero.Utilities.Translate.prototype = new tmp(); +Zotero.Utilities.Translate.prototype.formatDate = Zotero.Date.formatDate; +Zotero.Utilities.Translate.prototype.strToDate = Zotero.Date.strToDate; +Zotero.Utilities.Translate.prototype.strToISO = Zotero.Date.strToISO; +Zotero.Utilities.Translate.prototype.createContextObject = Zotero.OpenURL.createContextObject; +Zotero.Utilities.Translate.prototype.parseContextObject = Zotero.OpenURL.parseContextObject; + /** * Gets the current Zotero version * @@ -769,7 +652,7 @@ Zotero.Utilities.Translate.prototype.loadDocument = function(url, succeeded, fai } /** - * Already documented in Zotero.Utilities.HTTP + * Already documented in Zotero.HTTP * @ignore */ Zotero.Utilities.Translate.prototype.processDocuments = function(urls, processor, done, exception) { @@ -792,8 +675,7 @@ Zotero.Utilities.Translate.prototype.processDocuments = function(urls, processor } } - var hiddenBrowser = Zotero.Utilities.HTTP.processDocuments(urls, processor, done, exception); - if(this.translate.cookieManager) this.translate.cookieManager.attachToBrowser(hiddenBrowser); + Zotero.HTTP.processDocuments(urls, processor, done, exception); } /** @@ -813,7 +695,6 @@ Zotero.Utilities.Translate.prototype.retrieveDocument = function(url) { } var hiddenBrowser = Zotero.Browser.createHiddenBrowser(); - if(this.translate.cookieManager) this.translate.cookieManager.attachToBrowser(hiddenBrowser); hiddenBrowser.addEventListener("pageshow", listener, true); hiddenBrowser.loadURI(url); @@ -860,15 +741,11 @@ Zotero.Utilities.Translate.prototype.retrieveSource = function(url, body, header var listener = function(aXmlhttp) { xmlhttp = aXmlhttp }; if(body) { - var xhr = Zotero.Utilities.HTTP.doPost(url, body, listener, headers, responseCharset, true); + Zotero.HTTP.doPost(url, body, listener, headers, responseCharset); } else { - var xhr = Zotero.Utilities.HTTP.doGet(url, listener, responseCharset, true); + Zotero.HTTP.doGet(url, listener, responseCharset); } - if(this.translate.cookieManager) this.translate.cookieManager.attachToXHR(xhr); - - xhr.send(body ? body : null); - while(!xmlhttp) mainThread.processNextEvent(true); if(xmlhttp.status >= 400) throw "retrieveSource failed: "+xmlhttp.status+" "+xmlhttp.statusText; @@ -898,7 +775,7 @@ Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, res var me = this; - var xhr = Zotero.Utilities.HTTP.doGet(url, function(xmlhttp) { + Zotero.HTTP.doGet(url, function(xmlhttp) { try { if(processor) { processor(xmlhttp.responseText, xmlhttp, url); @@ -914,30 +791,68 @@ Zotero.Utilities.Translate.prototype.doGet = function(urls, processor, done, res } catch(e) { me.translate.error(false, e); } - }, responseCharset, true); - - if(this.translate.cookieManager) this.translate.cookieManager.attachToXHR(xhr); - xhr.send(null); + }, responseCharset); } /** - * Already documented in Zotero.Utilities.HTTP + * Already documented in Zotero.HTTP * @ignore */ Zotero.Utilities.Translate.prototype.doPost = function(url, body, onDone, headers, responseCharset) { url = this._convertURL(url); var translate = this.translate; - var xhr = Zotero.Utilities.HTTP.doPost(url, body, function(xmlhttp) { + Zotero.HTTP.doPost(url, body, function(xmlhttp) { try { onDone(xmlhttp.responseText, xmlhttp); } catch(e) { translate.error(false, e); } - }, headers, responseCharset, true); - - if(this.translate.cookieManager) this.translate.cookieManager.attachToXHR(xhr); - xhr.send(body); + }, headers, responseCharset); +} + +/** + * Tests if an item type exists + * + * @param {String} type Item type + * @type Boolean + */ +Zotero.Utilities.Translate.prototype.itemTypeExists = function(type) { + if(Zotero.ItemTypes.getID(type)) { + return true; + } else { + return false; + } +} + +/** + * Find valid creator types for a given item type + * + * @param {String} type Item type + * @return {String[]} Creator types + */ +Zotero.Utilities.Translate.prototype.getCreatorsForType = function(type) { + var types = Zotero.CreatorTypes.getTypesForItemType(Zotero.ItemTypes.getID(type)); + var cleanTypes = new Array(); + for each(var type in types) { + cleanTypes.push(type.name); + } + return cleanTypes; +} + +/** + * Gets a creator type name, localized to the current locale + * + * @param {String} type Creator type + * @param {String} Localized creator type + * @type Boolean + */ +Zotero.Utilities.Translate.prototype.getLocalizedCreatorType = function(type) { + try { + return Zotero.getString("creatorTypes."+type); + } catch(e) { + return false; + } } /** @@ -966,628 +881,88 @@ Zotero.Utilities.Translate.prototype._convertURL = function(url) { } /** - * Wrap all functions so that arguments are guaranteed safe + * @class Utility functions not made available to translators */ -borrowedFunctions = { - "formatDate":Zotero.Date.formatDate, - "strToDate":Zotero.Date.strToDate, - "strToISO":Zotero.Date.strToISO, - "createContextObject":Zotero.OpenURL.createContextObject, - "parseContextObject":Zotero.OpenURL.parseContextObject -}; -for each(var wrapArrayBad in [borrowedFunctions, Zotero.Utilities.prototype, Zotero.Utilities.Translate.prototype]) { - let wrapArray = wrapArrayBad; - for(var wrapFunctionBad in wrapArray) { - let wrapFunction = wrapFunctionBad; - if(!(wrapArray[wrapFunction] instanceof Function)) continue; - - let unwrappedFunction = wrapArray[wrapFunction]; - - Zotero.Utilities.Translate.prototype[wrapFunction] = function() { - let args = []; - for(let i=0; i<arguments.length; i++) { - if(typeof arguments[i] != "object" - && arguments[i] instanceof XPCSafeJSObjectWrapper - && arguments[i] !== null) { - args.push(new XPCSafeJSObjectWrapper(arguments[i])); - } else { - args.push(arguments[i]); - } - } - return unwrappedFunction.apply(this, args); - } - } -} - -/** - * Functions for performing HTTP requests, both via XMLHTTPRequest and using a hidden browser - * @namespace - */ -Zotero.Utilities.HTTP = new function() { - this.WebDAV = {}; - - - /** - * Send an HTTP GET request via XMLHTTPRequest - * - * @param {nsIURI|String} url URL to request - * @param {Function} onDone Callback to be executed upon request completion. - * If false, XHR is returned unsent. - * @param {String} responseCharset Character set to force on the response - * @param {Boolean} dontSend Don't send XHR before returning - * @return {Boolean} True if the request was sent, or false if the browser is offline - */ - this.doGet = function(url, onDone, responseCharset, dontSend) { - if (url instanceof Components.interfaces.nsIURI) { - // Don't display password in console - var disp = url.clone(); - if (disp.password) { - disp.password = "********"; - } - Zotero.debug("HTTP GET " + disp.spec); - url = url.spec; - } - else { - Zotero.debug("HTTP GET " + url); - } - if (this.browserIsOffline()){ - return false; - } - - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - // Prevent certificate/authentication dialogs from popping up - xmlhttp.mozBackgroundRequest = true; - xmlhttp.open('GET', url, true); - // Send cookie even if "Allow third-party cookies" is disabled (>=Fx3.6 only) - if (!Zotero.isFx35) { - var channel = xmlhttp.channel; - channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal); - channel.forceAllowThirdPartyCookie = true; - } - - // Don't cache GET requests - xmlhttp.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; - - /** @ignore */ - xmlhttp.onreadystatechange = function() { - _stateChange(xmlhttp, onDone, responseCharset); - }; - - if(!dontSend) { - xmlhttp.send(null); - } - - return xmlhttp; - } - - /** - * Send an HTTP POST request via XMLHTTPRequest - * - * @param {String} url URL to request - * @param {String} body Request body - * @param {Function} onDone Callback to be executed upon request completion. - * @param {String} headers Request HTTP headers - * @param {String} responseCharset Character set to force on the response - * @param {Boolean} dontSend Don't send XHR before returning - * @return {Boolean} True if the request was sent, or false if the browser is offline - */ - this.doPost = function(url, body, onDone, headers, responseCharset, dontSend) { - if (url instanceof Components.interfaces.nsIURI) { - // Don't display password in console - var disp = url.clone(); - if (disp.password) { - disp.password = "********"; - } - url = url.spec; - } - - var bodyStart = body.substr(0, 1024); - // Don't display sync password or session id in console - bodyStart = bodyStart.replace(/password=[^&]+/, 'password=********'); - bodyStart = bodyStart.replace(/sessionid=[^&]+/, 'sessionid=********'); - - Zotero.debug("HTTP POST " - + (body.length > 1024 ? - bodyStart + '... (' + body.length + ' chars)' : bodyStart) - + " to " + (disp ? disp.spec : url)); - - - if (this.browserIsOffline()){ - return false; - } - - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - // Prevent certificate/authentication dialogs from popping up - xmlhttp.mozBackgroundRequest = true; - xmlhttp.open('POST', url, true); - // Send cookie even if "Allow third-party cookies" is disabled (>=Fx3.6 only) - if (!Zotero.isFx35) { - var channel = xmlhttp.channel; - channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal); - channel.forceAllowThirdPartyCookie = true; - } - - if (headers) { - if (typeof headers == 'string') { - var msg = "doPost() now takes a headers object rather than a requestContentType -- update your code"; - Zotero.debug(msg, 2); - Components.utils.reportError(msg); - headers = { - "Content-Type": headers - }; - } - } - else { - headers = {}; - } - - if (!headers["Content-Type"]) { - headers["Content-Type"] = "application/x-www-form-urlencoded"; - } - - for (var header in headers) { - xmlhttp.setRequestHeader(header, headers[header]); - } - - /** @ignore */ - xmlhttp.onreadystatechange = function(){ - _stateChange(xmlhttp, onDone, responseCharset); - }; - - if(!dontSend) { - xmlhttp.send(body); - } - - return xmlhttp; - } - - /** - * Send an HTTP HEAD request via XMLHTTPRequest - * - * @param {String} url URL to request - * @param {Function} onDone Callback to be executed upon request completion - * @param {Object} requestHeaders HTTP headers to include with request - * @return {Boolean} True if the request was sent, or false if the browser is offline - */ - this.doHead = function(url, onDone, requestHeaders) { - if (url instanceof Components.interfaces.nsIURI) { - // Don't display password in console - var disp = url.clone(); - if (disp.password) { - disp.password = "********"; - } - Zotero.debug("HTTP HEAD " + disp.spec); - url = url.spec; - } - else { - Zotero.debug("HTTP HEAD " + url); - - } - - if (this.browserIsOffline()){ - return false; - } - - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - // Prevent certificate/authentication dialogs from popping up - xmlhttp.mozBackgroundRequest = true; - xmlhttp.open('HEAD', url, true); - // Send cookie even if "Allow third-party cookies" is disabled (>=Fx3.6 only) - if (!Zotero.isFx35) { - var channel = xmlhttp.channel; - channel.QueryInterface(Components.interfaces.nsIHttpChannelInternal); - channel.forceAllowThirdPartyCookie = true; - } - - if (requestHeaders) { - for (var header in requestHeaders) { - xmlhttp.setRequestHeader(header, requestHeaders[header]); - } - } - - // Don't cache HEAD requests - xmlhttp.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE; - - /** @ignore */ - xmlhttp.onreadystatechange = function(){ - _stateChange(xmlhttp, onDone); - }; - - xmlhttp.send(null); - - return xmlhttp; - } - - /** - * Send an HTTP OPTIONS request via XMLHTTPRequest +Zotero.Utilities.Internal = { + /* + * Adapted from http://developer.mozilla.org/en/docs/nsICryptoHash * - * @param {nsIURI} url - * @param {Function} onDone - * @return {XMLHTTPRequest} + * @param {String|nsIFile} strOrFile + * @param {Boolean} [base64=false] Return as base-64-encoded string rather than hex string + * @return {String} */ - this.doOptions = function (uri, callback) { - // Don't display password in console - var disp = uri.clone(); - if (disp.password) { - disp.password = "********"; - } - Zotero.debug("HTTP OPTIONS for " + disp.spec); - - if (Zotero.Utilities.HTTP.browserIsOffline()){ - return false; - } - - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - // Prevent certificate/authentication dialogs from popping up - xmlhttp.mozBackgroundRequest = true; - xmlhttp.open('OPTIONS', uri.spec, true); - /** @ignore */ - xmlhttp.onreadystatechange = function() { - _stateChange(xmlhttp, callback); - }; - xmlhttp.send(null); - return xmlhttp; - } - - // - // WebDAV methods - // - - /** - * Send a WebDAV PROP* request via XMLHTTPRequest - * - * Returns false if browser is offline - * - * @param {String} method PROPFIND or PROPPATCH - * @param {nsIURI} uri - * @param {String} body XML string - * @param {Function} callback - * @param {Object} requestHeaders e.g. { Depth: 0 } - */ - this.WebDAV.doProp = function (method, uri, body, callback, requestHeaders) { - switch (method) { - case 'PROPFIND': - case 'PROPPATCH': - break; - - default: - throw ("Invalid method '" + method - + "' in Zotero.Utilities.HTTP.doProp"); - } - - if (requestHeaders && requestHeaders.depth != undefined) { - var depth = requestHeaders.depth; - } - - // Don't display password in console - var disp = uri.clone(); - if (disp.password) { - disp.password = "********"; - } - - var bodyStart = body.substr(0, 1024); - Zotero.debug("HTTP " + method + " " - + (depth != undefined ? "(depth " + depth + ") " : "") - + (body.length > 1024 ? - bodyStart + "... (" + body.length + " chars)" : bodyStart) - + " to " + disp.spec); - - if (Zotero.Utilities.HTTP.browserIsOffline()) { - Zotero.debug("Browser is offline", 2); - return false; + "md5":function(strOrFile, base64) { + if (typeof strOrFile == 'string') { + var converter = Components.classes["@mozilla.org/intl/scriptableunicodeconverter"]. + createInstance(Components.interfaces.nsIScriptableUnicodeConverter); + converter.charset = "UTF-8"; + var result = {}; + var data = converter.convertToByteArray(strOrFile, result); + var ch = Components.classes["@mozilla.org/security/hash;1"] + .createInstance(Components.interfaces.nsICryptoHash); + ch.init(ch.MD5); + ch.update(data, data.length); } - - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - // Prevent certificate/authentication dialogs from popping up - xmlhttp.mozBackgroundRequest = true; - xmlhttp.open(method, uri.spec, true); - - if (requestHeaders) { - for (var header in requestHeaders) { - xmlhttp.setRequestHeader(header, requestHeaders[header]); + else if (strOrFile instanceof Components.interfaces.nsIFile) { + // Otherwise throws (NS_ERROR_NOT_AVAILABLE) [nsICryptoHash.updateFromStream] + if (!strOrFile.fileSize) { + // MD5 for empty string + return "d41d8cd98f00b204e9800998ecf8427e"; } + + var istream = Components.classes["@mozilla.org/network/file-input-stream;1"] + .createInstance(Components.interfaces.nsIFileInputStream); + // open for reading + istream.init(strOrFile, 0x01, 0444, 0); + var ch = Components.classes["@mozilla.org/security/hash;1"] + .createInstance(Components.interfaces.nsICryptoHash); + // we want to use the MD5 algorithm + ch.init(ch.MD5); + // this tells updateFromStream to read the entire file + const PR_UINT32_MAX = 0xffffffff; + ch.updateFromStream(istream, PR_UINT32_MAX); } - xmlhttp.setRequestHeader("Content-Type", 'text/xml; charset="utf-8"'); - - xmlhttp.onreadystatechange = function() { - _stateChange(xmlhttp, callback); - }; - - xmlhttp.send(body); - return xmlhttp; - } - - - /** - * Send a WebDAV MKCOL request via XMLHTTPRequest - * - * @param {nsIURI} url - * @param {Function} onDone - * @return {XMLHTTPRequest} - */ - this.WebDAV.doMkCol = function (uri, callback) { - // Don't display password in console - var disp = uri.clone(); - if (disp.password) { - disp.password = "********"; - } - Zotero.debug("HTTP MKCOL " + disp.spec); - - if (Zotero.Utilities.HTTP.browserIsOffline()) { - return false; - } - - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - // Prevent certificate/authentication dialogs from popping up - xmlhttp.mozBackgroundRequest = true; - xmlhttp.open('MKCOL', uri.spec, true); - xmlhttp.onreadystatechange = function() { - _stateChange(xmlhttp, callback); - }; - xmlhttp.send(null); - return xmlhttp; - } - - - /** - * Send a WebDAV PUT request via XMLHTTPRequest - * - * @param {nsIURI} url - * @param {String} body String body to PUT - * @param {Function} onDone - * @return {XMLHTTPRequest} - */ - this.WebDAV.doPut = function (uri, body, callback) { - // Don't display password in console - var disp = uri.clone(); - if (disp.password) { - disp.password = "********"; - } - - var bodyStart = "'" + body.substr(0, 1024) + "'"; - Zotero.debug("HTTP PUT " - + (body.length > 1024 ? - bodyStart + "... (" + body.length + " chars)" : bodyStart) - + " to " + disp.spec); + // pass false here to get binary data back + var hash = ch.finish(base64); - if (Zotero.Utilities.HTTP.browserIsOffline()) { - return false; + if (istream) { + istream.close(); } - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - // Prevent certificate/authentication dialogs from popping up - xmlhttp.mozBackgroundRequest = true; - xmlhttp.open("PUT", uri.spec, true); - // Some servers (e.g., Jungle Disk DAV) return a 200 response code - // with Content-Length: 0, which triggers a "no element found" error - // in Firefox, so we override to text - xmlhttp.overrideMimeType("text/plain"); - xmlhttp.onreadystatechange = function() { - _stateChange(xmlhttp, callback); - }; - xmlhttp.send(body); - return xmlhttp; - } - - - /** - * Send a WebDAV PUT request via XMLHTTPRequest - * - * @param {nsIURI} url - * @param {Function} onDone - * @return {XMLHTTPRequest} - */ - this.WebDAV.doDelete = function (uri, callback) { - // Don't display password in console - var disp = uri.clone(); - if (disp.password) { - disp.password = "********"; + if (base64) { + return hash; } - Zotero.debug("WebDAV DELETE to " + disp.spec); - - if (Zotero.Utilities.HTTP.browserIsOffline()) { - return false; - } + /* + // This created 36-character hashes - var xmlhttp = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"] - .createInstance(); - // Prevent certificate/authentication dialogs from popping up - xmlhttp.mozBackgroundRequest = true; - xmlhttp.open("DELETE", uri.spec, true); - // Firefox 3 throws a "no element found" error even with a - // 204 ("No Content") response, so we override to text - xmlhttp.overrideMimeType("text/plain"); - xmlhttp.onreadystatechange = function() { - _stateChange(xmlhttp, callback); - }; - xmlhttp.send(null); - return xmlhttp; - } - - - /** - * Get the Authorization header used by a channel - * - * As of Firefox 3.0.1 subsequent requests to higher-level directories - * seem not to authenticate properly and just return 401s, so this - * can be used to manually include the Authorization header in a request - * - * It can also be used to check whether a request was forced to - * use authentication - * - * @param {nsIChannel} channel - * @return {String|FALSE} Authorization header, or FALSE if none - */ - this.getChannelAuthorization = function (channel) { - try { - channel.QueryInterface(Components.interfaces.nsIHttpChannel); - var authHeader = channel.getRequestHeader("Authorization"); - return authHeader; + // return the two-digit hexadecimal code for a byte + function toHexString(charCode) { + return ("0" + charCode.toString(16)).slice(-2); } - catch (e) { - Zotero.debug(e); - return false; - } - } - - - /** - * Checks if the browser is currently in "Offline" mode - * - * @type Boolean - */ - this.browserIsOffline = function() { - return Components.classes["@mozilla.org/network/io-service;1"] - .getService(Components.interfaces.nsIIOService).offline; - } - - /** - * Load one or more documents in a hidden browser - * - * @param {String|String[]} urls URL(s) of documents to load - * @param {Function} processor Callback to be executed for each document loaded - * @param {Function} done Callback to be executed after all documents have been loaded - * @param {Function} exception Callback to be executed if an exception occurs - */ - this.processDocuments = function(urls, processor, done, exception) { - /** - * Removes event listener for the load event and deletes the hidden browser - */ - var removeListeners = function() { - hiddenBrowser.removeEventListener(loadEvent, onLoad, true); - Zotero.Browser.deleteHiddenBrowser(hiddenBrowser); - } - - /** - * Loads the next page - * @inner - */ - var doLoad = function() { - if(urls.length) { - var url = urls.shift(); - try { - Zotero.debug("loading "+url); - hiddenBrowser.loadURI(url); - } catch(e) { - removeListeners(); - if(exception) { - exception(e); - return; - } else { - throw(e); - } - } - } else { - removeListeners(); - if(done) done(); - } - }; - - /** - * Callback to be executed when a page load completes - * @inner - */ - var onLoad = function() { - if(hiddenBrowser.contentDocument.location.href == "about:blank") return; - Zotero.debug(hiddenBrowser.contentDocument.location.href+" has been loaded"); - if(hiddenBrowser.contentDocument.location.href != prevUrl) { // Just in case it fires too many times - prevUrl = hiddenBrowser.contentDocument.location.href; - try { - processor(hiddenBrowser.contentDocument); - } catch(e) { - removeListeners(); - if(exception) { - exception(e); - return; - } else { - throw(e); - } - } - doLoad(); - } - }; - - if(typeof(urls) == "string") urls = [urls]; - - var prevUrl; - var loadEvent = "pageshow"; - var hiddenBrowser = Zotero.Browser.createHiddenBrowser(); - hiddenBrowser.addEventListener(loadEvent, onLoad, true); - - doLoad(); - return hiddenBrowser; - } - - /** - * Handler for XMLHttpRequest state change - * - * @param {nsIXMLHttpRequest} XMLHttpRequest whose state just changed - * @param {Function} [onDone] Callback for request completion - * @param {String} [responseCharset] Character set to force on the response - * @private - */ - function _stateChange(xmlhttp, callback, responseCharset, data) { - switch (xmlhttp.readyState){ - // Request not yet made - case 1: - break; - - case 2: - break; - - // Called multiple times while downloading in progress - case 3: - break; - - // Download complete - case 4: - if (callback) { - // Override the content charset - if (responseCharset) { - xmlhttp.channel.contentCharset = responseCharset; - } - callback(xmlhttp, data); - } - break; - } - } -} - -/** - * @namespace - */ -// This would probably be better as a separate XPCOM service -Zotero.Utilities.AutoComplete = new function() { - this.getResultComment = function (textbox){ - var controller = textbox.controller; + // convert the binary hash data to a hex string. + return [toHexString(hash.charCodeAt(i)) for (i in hash)].join(""); + */ - for (var i=0; i<controller.matchCount; i++) - { - if (controller.getValueAt(i) == textbox.value) - { - return controller.getCommentAt(i); - } + // From http://rcrowley.org/2007/11/15/md5-in-xulrunner-or-firefox-extensions/ + var ascii = []; ii = hash.length; + for (var i = 0; i < ii; ++i) { + var c = hash.charCodeAt(i); + var ones = c % 16; + var tens = c >> 4; + ascii.push(String.fromCharCode(tens + (tens > 9 ? 87 : 48)) + String.fromCharCode(ones + (ones > 9 ? 87 : 48))); } - return false; + return ascii.join(''); } } - /** * Base64 encode / decode * From http://www.webtoolkit.info/ */ -Zotero.Utilities.Base64 = { +Zotero.Utilities.Internal.Base64 = { // private property _keyStr : "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=", diff --git a/chrome/content/zotero/xpcom/zeroconf.js b/chrome/content/zotero/xpcom/zeroconf.js @@ -336,7 +336,7 @@ Zotero.Zeroconf.RemoteLibrary.prototype.load = function () { Zotero.Zeroconf.RemoteLibrary.prototype.loadItems = function (self, noNotify) { var url = "http://" + this._host + ':' + this._port; - Zotero.Utilities.HTTP.doPost(url, '', function (xmlhttp) { + Zotero.HTTP.doPost(url, '', function (xmlhttp) { Zotero.debug(xmlhttp.responseText); self._items = []; diff --git a/chrome/content/zotero/xpcom/zotero.js b/chrome/content/zotero/xpcom/zotero.js @@ -1485,7 +1485,7 @@ Zotero.Prefs = new function(){ return; } - str = Zotero.Utilities.prototype.trim(str.replace(/<\?xml.*\?>\s*/, '')); + str = Zotero.Utilities.trim(str.replace(/<\?xml.*\?>\s*/, '')); Zotero.debug(str); var confirm = ps.confirm( @@ -1912,13 +1912,12 @@ Zotero.Date = new function(){ return date.toLocaleFormat('%Y-%m-%d %H:%M:%S'); } - var utils = new Zotero.Utilities(); - year = utils.lpad(year, '0', 4); - month = utils.lpad(month + 1, '0', 2); - day = utils.lpad(day, '0', 2); - hours = utils.lpad(hours, '0', 2); - minutes = utils.lpad(minutes, '0', 2); - seconds = utils.lpad(seconds, '0', 2); + year = Zotero.Utilities.lpad(year, '0', 4); + month = Zotero.Utilities.lpad(month + 1, '0', 2); + day = Zotero.Utilities.lpad(day, '0', 2); + hours = Zotero.Utilities.lpad(hours, '0', 2); + minutes = Zotero.Utilities.lpad(minutes, '0', 2); + seconds = Zotero.Utilities.lpad(seconds, '0', 2); return year + '-' + month + '-' + day + ' ' + hours + ':' + minutes + ':' + seconds; @@ -1945,13 +1944,12 @@ Zotero.Date = new function(){ var minutes = date.getUTCMinutes(); var seconds = date.getUTCSeconds(); - var utils = new Zotero.Utilities(); - year = utils.lpad(year, '0', 4); - month = utils.lpad(month + 1, '0', 2); - day = utils.lpad(day, '0', 2); - hours = utils.lpad(hours, '0', 2); - minutes = utils.lpad(minutes, '0', 2); - seconds = utils.lpad(seconds, '0', 2); + year = Zotero.Utilities.lpad(year, '0', 4); + month = Zotero.Utilities.lpad(month + 1, '0', 2); + day = Zotero.Utilities.lpad(day, '0', 2); + hours = Zotero.Utilities.lpad(hours, '0', 2); + minutes = Zotero.Utilities.lpad(minutes, '0', 2); + seconds = Zotero.Utilities.lpad(seconds, '0', 2); return year + '-' + month + '-' + day + 'T' + hours + ':' + minutes + ':' + seconds + 'Z'; @@ -2217,11 +2215,11 @@ Zotero.Date = new function(){ var date = Zotero.Date.strToDate(str); if(date.year) { - var dateString = Zotero.Utilities.prototype.lpad(date.year, "0", 4); + var dateString = Zotero.Utilities.lpad(date.year, "0", 4); if(date.month) { - dateString += "-"+Zotero.Utilities.prototype.lpad(date.month+1, "0", 2); + dateString += "-"+Zotero.Utilities.lpad(date.month+1, "0", 2); if(date.day) { - dateString += "-"+Zotero.Utilities.prototype.lpad(date.day, "0", 2); + dateString += "-"+Zotero.Utilities.lpad(date.day, "0", 2); } } return dateString; @@ -2234,8 +2232,6 @@ Zotero.Date = new function(){ return ''; } - var utils = new Zotero.Utilities(); - var parts = strToDate(str); // FIXME: Until we have a better BCE date solution, @@ -2249,9 +2245,9 @@ Zotero.Date = new function(){ parts.month = typeof parts.month != "undefined" ? parts.month + 1 : ''; - var multi = (parts.year ? utils.lpad(parts.year, '0', 4) : '0000') + '-' - + utils.lpad(parts.month, '0', 2) + '-' - + (parts.day ? utils.lpad(parts.day, '0', 2) : '00') + var multi = (parts.year ? Zotero.Utilities.lpad(parts.year, '0', 4) : '0000') + '-' + + Zotero.Utilities.lpad(parts.month, '0', 2) + '-' + + (parts.day ? Zotero.Utilities.lpad(parts.day, '0', 2) : '00') + ' ' + str; return multi; diff --git a/components/zotero-service.js b/components/zotero-service.js @@ -84,6 +84,7 @@ var xpcomFiles = [ 'error', 'file', 'fulltext', + 'http', 'id', 'ingester', 'integration',