diff --git a/chrome/content/zotero/bindings/itembox.xml b/chrome/content/zotero/bindings/itembox.xml
index 46416ecb4d..cb7ba5f757 100644
--- 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 @@
' + Zotero.Utilities.prototype.text2html(text) + ""; + + '>' + Zotero.Utilities.text2html(text) + ""; 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 index 934fb8cbde..3d2c700bae 100644 --- 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, '' + Zotero.Fulltext.pdfToolsName + '']); - var parts = utils.parseMarkup(str); + var parts = Zotero.Utilities.parseMarkup(str); for (var i=0; i
' + note.replace(/\n/g, '
')
.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
index 822531d888..ee483d5a89 100644
--- 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
index 5e2fdfae2d..efc65577e8 100644
--- 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
index 298b131c90..9b641910af 100644
--- 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
index 921c5c87a3..166efd2215 100644
--- 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
index 1753d3454b..14c7a0c969 100644
--- 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
new file mode 100644
index 0000000000..18b49990b1
--- /dev/null
+++ 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
index 9ab9834b68..e061a007c1 100644
--- 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
index 54ba586e9b..2171a93d05 100755
--- 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
index 0d54aac312..4120c171df 100644
--- 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
index 797337ec5a..e9c967a88c 100644
--- 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
index cacede8244..9f5d7bc51f 100644
--- 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
index 664cca57e1..1ec3bfe869 100644
--- 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
index 60bd6692e9..7912529db0 100644
--- 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
index 2c72fcf722..177b4f9878 100644
--- 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
index af4b429bc8..be66cdc8b5 100644
--- 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
index 755dde3e63..508c92b831 100644
--- 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
- if (singleNewlineIsParagraph) {
- str = ' '
- + str.replace(/\n/g, ' ')
- .replace(/\t/g, ' ')
- .replace(/ /g, ' ')
- + ' , \n => '
- + str.replace(/\n\n/g, ' ')
- .replace(/\n/g, ' \s*<\/p>/g, '
+ if (singleNewlineIsParagraph) {
+ str = ' '
+ + str.replace(/\n/g, ' ')
+ .replace(/\t/g, ' ')
+ .replace(/ /g, ' ')
+ + ' , \n => '
+ + str.replace(/\n\n/g, ' ')
+ .replace(/\n/g, ' \s*<\/p>/g, '
- else {
- str = Zotero.Utilities.prototype.htmlSpecialChars(str);
- str = '
')
- .replace(/\t/g, ' ')
- .replace(/ /g, ' ')
- + '
- *
- * Certain entities can be inserted manually:
- * <ZOTEROBREAK/> => <br/>
- * <ZOTEROHELLIP/> => …
- * @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 '
';
- case 'HELLIP':
- return '…';
- default:
- return p1;
+ /**
+ * 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);
+ },
+
+ /**
+ * 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]+$/, "");
+ },
- return newString;
-}
+ /**
+ * 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(/
]*>/gi, "\n");
+ return x.replace(/<[^>]+>/g, "");
+ },
-/**
- * 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);
-}
+ /**
+ * 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 x.match(/10\.[^\s\/]+\/[^\s]+/);
+ },
+ /**
+ * 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 =>
+ else {
+ str = Zotero.Utilities.htmlSpecialChars(str);
+ str = '
')
+ .replace(/\t/g, ' ')
+ .replace(/ /g, ' ')
+ + '
+ *
+ * Certain entities can be inserted manually:
+ * <ZOTEROBREAK/> => <br/>
+ * <ZOTEROHELLIP/> => …
+ * @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 '
';
+ case 'HELLIP':
+ return '…';
+ default:
+ return p1;
+ }
+ });
+
+ return newString;
+ },
+
+ /**
+ * 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$2');
- 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:
- * {
- * type: 'text'|'link',
- * text: "text content",
- * [ attributes: { key1: val [ , key2: val, ...] }
- * }
- */
-Zotero.Utilities.prototype.parseMarkup = function(/**String*/ str) {
- var parts = [];
- var splits = str.split(/(]+>[^<]*<\/a>)/);
+ /**
+ * Wrap URLs and DOIs in 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. "
+ // "
+ * {
+ * type: 'text'|'link',
+ * text: "text content",
+ * [ attributes: { key1: val [ , key2: val, ...] }
+ * }
+ */
+ "parseMarkup":function(/**String*/ str) {
+ var parts = [];
+ var splits = str.split(/(]+>[^<]*<\/a>)/);
+
+ for each(var split in splits) {
+ // Link
+ if (split.indexOf(']+)>([^<]*)<\/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
+ });
+ }
+
+ return parts;
+ },
+
+ /**
+ * 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;
+ }
+
+ 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));
}
}
- parts.push({
- type: 'text',
- text: split
- });
- }
+ return arr[aLen][bLen];
+ },
- 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;
-
- 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));
+ /**
+ * Test if an object is empty
+ *
+ * @param {Object} obj
+ * @type Boolean
+ */
+ "isEmpty":function (obj) {
+ for (var i in obj) {
+ return false;
}
- }
-
- return arr[aLen][bLen];
-}
+ return true;
+ },
-
-/**
- * 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