commit 7f5555aab6efb03226f38aecf524790aea00d801
parent 707a65c63e8a5ac816f38e4e175a83ce67f67609
Author: Aurimas Vinckevicius <aurimas.dev@gmail.com>
Date: Mon, 19 Jan 2015 11:00:11 -0600
Inheritance-based Zotero.DataObjects
Diffstat:
5 files changed, 684 insertions(+), 688 deletions(-)
diff --git a/chrome/content/zotero/xpcom/data/collections.js b/chrome/content/zotero/xpcom/data/collections.js
@@ -27,9 +27,10 @@
/*
* Primary interface for accessing Zotero collection
*/
-Zotero.Collections = new function() {
- Zotero.DataObjects.apply(this, ['collection']);
- this.constructor.prototype = new Zotero.DataObjects();
+Zotero.Collections = function() {
+ this.constructor = null;
+
+ this._ZDO_object = 'collection';
this._primaryDataSQLParts = {
collectionID: "O.collectionID",
@@ -45,9 +46,13 @@ Zotero.Collections = new function() {
hasChildCollections: "(SELECT COUNT(*) FROM collections WHERE "
+ "parentCollectionID=O.collectionID) != 0 AS hasChildCollections",
hasChildItems: "(SELECT COUNT(*) FROM collectionItems WHERE "
- + "collectionID=O.collectionID) != 0 AS hasChildItems "
+ + "collectionID=O.collectionID) != 0 AS hasChildItems"
};
+
+ this._primaryDataSQLFrom = "FROM collections O "
+ + "LEFT JOIN collections CP ON (O.parentCollectionID=CP.collectionID)";
+
/**
* Add new collection to DB and return Collection object
*
@@ -74,55 +79,51 @@ Zotero.Collections = new function() {
* Takes parent collectionID as optional parameter;
* by default, returns root collections
*/
- this.getByParent = Zotero.Promise.coroutine(function* (libraryID, parent, recursive) {
- var toReturn = [];
+ this.getByParent = Zotero.Promise.coroutine(function* (libraryID, parentID, recursive) {
+ let children;
- if (!parent) {
- parent = null;
+ if (parentID) {
+ let parent = yield this.getAsync(parentID);
+ yield parent.loadChildCollections();
+ children = parent.getChildCollections();
+ if (!children.length) Zotero.debug('No child collections in collection ' + parentID, 5);
+ } else if (libraryID || libraryID === 0) {
+ children = this.getCollectionsInLibrary(libraryID);
+ if (!children.length) Zotero.debug('No child collections in library ' + libraryID, 5);
+ } else {
+ throw new Error("Either library ID or parent collection ID must be provided to getNumCollectionsByParent");
}
- var sql = "SELECT collectionID AS id, collectionName AS name FROM collections C "
- + "WHERE libraryID=? AND parentCollectionID " + (parent ? '= ' + parent : 'IS NULL');
- var children = yield Zotero.DB.queryAsync(sql, [libraryID]);
-
- if (!children) {
- Zotero.debug('No child collections of collection ' + parent, 5);
- return toReturn;
+ if (!children.length) {
+ return children;
}
// Do proper collation sort
- var collation = Zotero.getLocaleCollation();
- children.sort(function (a, b) {
- return collation.compareString(1, a.name, b.name);
- });
+ children.sort(function (a, b) Zotero.localeCompare(a.name, b.name));
+
+ if (!recursive) return children;
+ let toReturn = [];
for (var i=0, len=children.length; i<len; i++) {
- var obj = yield this.getAsync(children[i].id);
- if (!obj) {
- throw ('Collection ' + children[i].id + ' not found');
- }
-
+ var obj = children[i];
toReturn.push(obj);
- // If recursive, get descendents
- if (recursive) {
- var desc = obj.getDescendents(false, 'collection');
- for (var j in desc) {
- var obj2 = yield this.getAsync(desc[j]['id']);
- if (!obj2) {
- throw new Error('Collection ' + desc[j] + ' not found');
- }
-
- // TODO: This is a quick hack so that we can indent subcollections
- // in the search dialog -- ideally collections would have a
- // getLevel() method, but there's no particularly quick way
- // of calculating that without either storing it in the DB or
- // changing the schema to Modified Preorder Tree Traversal,
- // and I don't know if we'll actually need it anywhere else.
- obj2.level = desc[j].level;
-
- toReturn.push(obj2);
+ var desc = obj.getDescendents(false, 'collection');
+ for (var j in desc) {
+ var obj2 = yield this.getAsync(desc[j]['id']);
+ if (!obj2) {
+ throw new Error('Collection ' + desc[j] + ' not found');
}
+
+ // TODO: This is a quick hack so that we can indent subcollections
+ // in the search dialog -- ideally collections would have a
+ // getLevel() method, but there's no particularly quick way
+ // of calculating that without either storing it in the DB or
+ // changing the schema to Modified Preorder Tree Traversal,
+ // and I don't know if we'll actually need it anywhere else.
+ obj2.level = desc[j].level;
+
+ toReturn.push(obj2);
}
}
@@ -130,6 +131,17 @@ Zotero.Collections = new function() {
});
+ this.getCollectionsInLibrary = Zotero.Promise.coroutine(function* (libraryID) {
+ let sql = "SELECT collectionID AS id FROM collections C "
+ + "WHERE libraryID=? AND parentCollectionId IS NULL";
+ let ids = yield Zotero.DB.queryAsync(sql, [libraryID]);
+ let collections = yield this.getAsync(ids.map(function(row) row.id));
+ if (!collections.length) return collections;
+
+ return collections.sort(function (a, b) Zotero.localeCompare(a.name, b.name));
+ });
+
+
this.getCollectionsContainingItems = function (itemIDs, asIDs) {
// If an unreasonable number of items, don't try
if (itemIDs.length > 100) {
@@ -145,8 +157,8 @@ Zotero.Collections = new function() {
}
sql = sql.substring(0, sql.length - 5);
return Zotero.DB.columnQueryAsync(sql, sqlParams)
- .then(function (collectionIDs) {
- return asIDs ? collectionIDs : Zotero.Collections.get(collectionIDs);
+ .then(collectionIDs => {
+ return asIDs ? collectionIDs : this.get(collectionIDs);
});
}
@@ -186,32 +198,23 @@ Zotero.Collections = new function() {
});
- this.erase = function (ids) {
+ this.erase = function(ids) {
ids = Zotero.flattenArguments(ids);
- Zotero.DB.beginTransaction();
- for each(var id in ids) {
- var collection = this.getAsync(id);
- if (collection) {
- collection.erase();
+ return Zotero.DB.executeTransaction(function* () {
+ for each(var id in ids) {
+ var collection = yield this.getAsync(id);
+ if (collection) {
+ yield collection.erase();
+ }
+ collection = undefined;
}
- collection = undefined;
- }
-
- this.unload(ids);
-
- Zotero.DB.commitTransaction();
- }
+
+ this.unload(ids);
+ });
+ };
+ Zotero.DataObjects.call(this);
- this.getPrimaryDataSQL = function () {
- // This should be the same as the query in Zotero.Collection.load(),
- // just without a specific collectionID
- return "SELECT "
- + Object.keys(this._primaryDataSQLParts).map(key => this._primaryDataSQLParts[key]).join(", ") + " "
- + "FROM collections O "
- + "LEFT JOIN collections CP ON (O.parentCollectionID=CP.collectionID) "
- + "WHERE 1";
- }
-}
-
+ return this;
+}.bind(Object.create(Zotero.DataObjects.prototype))();
diff --git a/chrome/content/zotero/xpcom/data/dataObjects.js b/chrome/content/zotero/xpcom/data/dataObjects.js
@@ -24,610 +24,608 @@
*/
-Zotero.DataObjects = function (object, objectPlural, id, table) {
- var self = this;
-
- if (!object) {
- object = '';
- }
-
- // Override these variables in child objects
- this._ZDO_object = object;
- this._ZDO_objects = objectPlural ? objectPlural : object + 's';
- this._ZDO_Object = object.substr(0, 1).toUpperCase() + object.substr(1);
- this._ZDO_Objects = this._ZDO_objects.substr(0, 1).toUpperCase()
- + this._ZDO_objects.substr(1);
- this._ZDO_id = (id ? id : object) + 'ID';
- this._ZDO_table = table ? table : this._ZDO_objects;
-
- // Certain object types don't have a libary and key and only use an id
- switch (object) {
- case 'relation':
- this._ZDO_idOnly = true;
- break;
-
- default:
- this._ZDO_idOnly = false;
- }
-
- Zotero.defineProperty(this, 'idColumn', {
- get: function() this._ZDO_id
- });
+Zotero.DataObjects = function () {
+ if (!this._ZDO_object) throw new Error('this._ZDO_object must be set before calling Zotero.DataObjects constructor');
+
+ if (!this._ZDO_objects) {
+ this._ZDO_objects = Zotero.DataObjectUtilities.getObjectTypePlural(this._ZDO_object);
+ }
+ if (!this._ZDO_Object) {
+ this._ZDO_Object = this._ZDO_object.substr(0, 1).toUpperCase()
+ + this._ZDO_object.substr(1);
+ }
+ if (!this._ZDO_Objects) {
+ this._ZDO_Objects = this._ZDO_objects.substr(0, 1).toUpperCase()
+ + this._ZDO_objects.substr(1);
+ }
+
+ if (!this._ZDO_id) {
+ this._ZDO_id = this._ZDO_object + 'ID';
+ }
+
+ if (!this._ZDO_table) {
+ this._ZDO_table = this._ZDO_objects;
+ }
+
+ if (!this.ObjectClass) {
+ this.ObjectClass = Zotero[this._ZDO_Object];
+ }
+
+ this.primaryDataSQLFrom = " " + this._primaryDataSQLFrom + " " + this._primaryDataSQLWhere;
this._objectCache = {};
this._objectKeys = {};
this._objectIDs = {};
this._loadedLibraries = {};
this._loadPromise = null;
+}
+
+Zotero.DataObjects.prototype._ZDO_idOnly = false;
+
+// Public properties
+Zotero.defineProperty(Zotero.DataObjects.prototype, 'idColumn', {
+ get: function() this._ZDO_id
+});
+Zotero.defineProperty(Zotero.DataObjects.prototype, 'table', {
+ get: function() this._ZDO_table
+});
+
+Zotero.defineProperty(Zotero.DataObjects.prototype, 'primaryFields', {
+ get: function () Object.keys(this._primaryDataSQLParts)
+}, {lazy: true});
+
+
+Zotero.DataObjects.prototype.init = function() {
+ return this._loadIDsAndKeys();
+}
+
+
+Zotero.DataObjects.prototype.isPrimaryField = function (field) {
+ return this.primaryFields.indexOf(field) != -1;
+}
+
+
+/**
+ * Retrieves one or more already-loaded items
+ *
+ * If an item hasn't been loaded, an error is thrown
+ *
+ * @param {Array|Integer} ids An individual object id or an array of object ids
+ * @return {Zotero.[Object]|Array<Zotero.[Object]>} A Zotero.[Object], if a scalar id was passed;
+ * otherwise, an array of Zotero.[Object]
+ */
+Zotero.DataObjects.prototype.get = function (ids) {
+ if (Array.isArray(ids)) {
+ var singleObject = false;
+ }
+ else {
+ var singleObject = true;
+ ids = [ids];
+ }
- // Public properties
- this.table = this._ZDO_table;
+ var toReturn = [];
+ for (let i=0; i<ids.length; i++) {
+ let id = ids[i];
+ // Check if already loaded
+ if (!this._objectCache[id]) {
+ throw new Zotero.Exception.UnloadedDataException(this._ZDO_Object + " " + id + " not yet loaded");
+ }
+ toReturn.push(this._objectCache[id]);
+ }
- this.init = function () {
- return this._loadIDsAndKeys();
+ // If single id, return the object directly
+ if (singleObject) {
+ return toReturn.length ? toReturn[0] : false;
}
+ return toReturn;
+};
- this.__defineGetter__('primaryFields', function () {
- var primaryFields = Object.keys(this._primaryDataSQLParts);
-
- // Once primary fields have been cached, get rid of getter for speed purposes
- delete this.primaryFields;
- this.primaryFields = primaryFields;
-
- return primaryFields;
- });
+/**
+ * Retrieves (and loads, if necessary) one or more items
+ *
+ * @param {Array|Integer} ids An individual object id or an array of object ids
+ * @param {Object} options 'noCache': Don't cache loaded objects
+ * @return {Zotero.[Object]|Array<Zotero.[Object]>} A Zotero.[Object], if a scalar id was passed;
+ * otherwise, an array of Zotero.[Object]
+ */
+Zotero.DataObjects.prototype.getAsync = Zotero.Promise.coroutine(function* (ids, options) {
+ var toLoad = [];
+ var toReturn = [];
- this.isPrimaryField = function (field) {
- return this.primaryFields.indexOf(field) != -1;
+ if (!ids) {
+ throw new Error("No arguments provided to " + this._ZDO_Objects + ".get()");
}
+ if (Array.isArray(ids)) {
+ var singleObject = false;
+ }
+ else {
+ var singleObject = true;
+ ids = [ids];
+ }
- /**
- * Retrieves one or more already-loaded items
- *
- * If an item hasn't been loaded, an error is thrown
- *
- * @param {Array|Integer} ids An individual object id or an array of object ids
- * @return {Zotero.[Object]|Array<Zotero.[Object]>} A Zotero.[Object], if a scalar id was passed;
- * otherwise, an array of Zotero.[Object]
- */
- this.get = function (ids) {
- if (Array.isArray(ids)) {
- var singleObject = false;
- }
- else {
- var singleObject = true;
- ids = [ids];
- }
-
- var toReturn = [];
-
- for (let i=0; i<ids.length; i++) {
- let id = ids[i];
- // Check if already loaded
- if (!this._objectCache[id]) {
- throw new Zotero.Exception.UnloadedDataException(this._ZDO_Object + " " + id + " not yet loaded");
- }
+ for (let i=0; i<ids.length; i++) {
+ let id = ids[i];
+ // Check if already loaded
+ if (this._objectCache[id]) {
toReturn.push(this._objectCache[id]);
}
-
- // If single id, return the object directly
- if (singleObject) {
- return toReturn.length ? toReturn[0] : false;
+ else {
+ toLoad.push(id);
}
-
- return toReturn;
- };
-
+ }
- /**
- * Retrieves (and loads, if necessary) one or more items
- *
- * @param {Array|Integer} ids An individual object id or an array of object ids
- * @param {Object} options 'noCache': Don't cache loaded objects
- * @return {Zotero.[Object]|Array<Zotero.[Object]>} A Zotero.[Object], if a scalar id was passed;
- * otherwise, an array of Zotero.[Object]
- */
- this.getAsync = Zotero.Promise.coroutine(function* (ids, options) {
+ // New object to load
+ if (toLoad.length) {
// Serialize loads
if (this._loadPromise && this._loadPromise.isPending()) {
yield this._loadPromise;
}
- var deferred = Zotero.Promise.defer();
+ let deferred = Zotero.Promise.defer();
this._loadPromise = deferred.promise;
- var toLoad = [];
- var toReturn = [];
-
- if (!ids) {
- throw new Error("No arguments provided to " + this._ZDO_Objects + ".get()");
- }
-
- if (Array.isArray(ids)) {
- var singleObject = false;
- }
- else {
- var singleObject = true;
- ids = [ids];
- }
-
- for (let i=0; i<ids.length; i++) {
- let id = ids[i];
- // Check if already loaded
- if (this._objectCache[id]) {
- toReturn.push(this._objectCache[id]);
- }
- else {
- toLoad.push(id);
- }
- }
-
- // New object to load
- if (toLoad.length) {
- let loaded = yield this._load(null, toLoad, options);
- for (let i=0; i<toLoad.length; i++) {
- let id = toLoad[i];
- let obj = loaded[id];
- if (!obj) {
- Zotero.debug(this._ZDO_Object + " " + id + " doesn't exist", 2);
- continue;
- }
- toReturn.push(obj);
+ let loaded = yield this._load(null, toLoad, options);
+ for (let i=0; i<toLoad.length; i++) {
+ let id = toLoad[i];
+ let obj = loaded[id];
+ if (!obj) {
+ Zotero.debug(this._ZDO_Object + " " + id + " doesn't exist", 2);
+ continue;
}
+ toReturn.push(obj);
}
-
deferred.resolve();
-
- // If single id, return the object directly
- if (singleObject) {
- return toReturn.length ? toReturn[0] : false;
- }
-
- return toReturn;
- });
-
-
- /**
- * @deprecated - use .libraryKey
- */
- this.makeLibraryKeyHash = function (libraryID, key) {
- Zotero.debug("WARNING: Zotero.DataObjects.makeLibraryKeyHash() is deprecated -- use obj.libraryKey instead");
- return libraryID + '_' + key;
}
-
- /**
- * @deprecated - use .libraryKey
- */
- this.getLibraryKeyHash = function (obj) {
- Zotero.debug("WARNING: Zotero.DataObjects.getLibraryKeyHash() is deprecated -- use obj.libraryKey instead");
- return this.makeLibraryKeyHash(obj.libraryID, obj.key);
+ // If single id, return the object directly
+ if (singleObject) {
+ return toReturn.length ? toReturn[0] : false;
}
-
- this.parseLibraryKey = function (libraryKey) {
- var [libraryID, key] = libraryKey.split('/');
- return {
- libraryID: parseInt(libraryID),
- key: key
- };
+ return toReturn;
+});
+
+
+/**
+ * @deprecated - use .libraryKey
+ */
+Zotero.DataObjects.prototype.makeLibraryKeyHash = function (libraryID, key) {
+ Zotero.debug("WARNING: " + this._ZDO_Objects + ".makeLibraryKeyHash() is deprecated -- use .libraryKey instead");
+ return libraryID + '_' + key;
+}
+
+
+/**
+ * @deprecated - use .libraryKey
+ */
+Zotero.DataObjects.prototype.getLibraryKeyHash = function (obj) {
+ Zotero.debug("WARNING: " + this._ZDO_Objects + ".getLibraryKeyHash() is deprecated -- use .libraryKey instead");
+ return this.makeLibraryKeyHash(obj.libraryID, obj.key);
+}
+
+
+Zotero.DataObjects.prototype.parseLibraryKey = function (libraryKey) {
+ var [libraryID, key] = libraryKey.split('/');
+ return {
+ libraryID: parseInt(libraryID),
+ key: key
+ };
+}
+
+
+/**
+ * @deprecated - Use Zotero.DataObjects.parseLibraryKey()
+ */
+Zotero.DataObjects.prototype.parseLibraryKeyHash = function (libraryKey) {
+ Zotero.debug("WARNING: " + this._ZDO_Objects + ".parseLibraryKeyHash() is deprecated -- use .parseLibraryKey() instead");
+ var [libraryID, key] = libraryKey.split('_');
+ if (!key) {
+ return false;
}
-
-
- /**
- * @deprecated - Use Zotero.DataObjects.parseLibraryKey()
- */
- this.parseLibraryKeyHash = function (libraryKey) {
- Zotero.debug("WARNING: Zotero.DataObjects.parseLibraryKeyHash() is deprecated -- use .parseLibraryKey() instead");
- var [libraryID, key] = libraryKey.split('_');
- if (!key) {
- return false;
- }
- return {
- libraryID: parseInt(libraryID),
- key: key
- };
- }
-
-
- /**
- * Retrieves an object by its libraryID and key
- *
- * @param {Integer} libraryID
- * @param {String} key
- * @return {Zotero.DataObject} Zotero data object, or FALSE if not found
- */
- this.getByLibraryAndKey = function (libraryID, key, options) {
- var id = this.getIDFromLibraryAndKey(libraryID, key);
- if (!id) {
- return false;
- }
- return Zotero[this._ZDO_Objects].get(id, options);
+ return {
+ libraryID: parseInt(libraryID),
+ key: key
};
-
-
- /**
- * Asynchronously retrieves an object by its libraryID and key
- *
- * @param {Integer} - libraryID
- * @param {String} - key
- * @return {Promise<Zotero.DataObject>} - Promise for a data object, or FALSE if not found
- */
- this.getByLibraryAndKeyAsync = Zotero.Promise.coroutine(function* (libraryID, key, options) {
- var id = this.getIDFromLibraryAndKey(libraryID, key);
- if (!id) {
- return false;
- }
- return Zotero[this._ZDO_Objects].getAsync(id, options);
- });
-
-
- this.exists = function (itemID) {
- return !!this.getLibraryAndKeyFromID(itemID);
+}
+
+
+/**
+ * Retrieves an object by its libraryID and key
+ *
+ * @param {Integer} libraryID
+ * @param {String} key
+ * @return {Zotero.DataObject} Zotero data object, or FALSE if not found
+ */
+Zotero.DataObjects.prototype.getByLibraryAndKey = function (libraryID, key, options) {
+ var id = this.getIDFromLibraryAndKey(libraryID, key);
+ if (!id) {
+ return false;
}
-
-
- /**
- * @return {Array} Array with libraryID and key
- */
- this.getLibraryAndKeyFromID = function (id) {
- return this._objectKeys[id] ? this._objectKeys[id] : false;
+ return Zotero[this._ZDO_Objects].get(id, options);
+};
+
+
+/**
+ * Asynchronously retrieves an object by its libraryID and key
+ *
+ * @param {Integer} - libraryID
+ * @param {String} - key
+ * @return {Promise<Zotero.DataObject>} - Promise for a data object, or FALSE if not found
+ */
+Zotero.DataObjects.prototype.getByLibraryAndKeyAsync = Zotero.Promise.coroutine(function* (libraryID, key, options) {
+ var id = this.getIDFromLibraryAndKey(libraryID, key);
+ if (!id) {
+ return false;
+ }
+ return Zotero[this._ZDO_Objects].getAsync(id, options);
+});
+
+
+Zotero.DataObjects.prototype.exists = function (itemID) {
+ return !!this.getLibraryAndKeyFromID(itemID);
+}
+
+
+/**
+ * @return {Array} Array with libraryID and key
+ */
+Zotero.DataObjects.prototype.getLibraryAndKeyFromID = function (id) {
+ return this._objectKeys[id] ? this._objectKeys[id] : false;
+}
+
+
+Zotero.DataObjects.prototype.getIDFromLibraryAndKey = function (libraryID, key) {
+ if (libraryID === null) {
+ throw new Error("libraryID cannot be NULL (did you mean 0?)");
+ }
+ return (this._objectIDs[libraryID] && this._objectIDs[libraryID][key])
+ ? this._objectIDs[libraryID][key] : false;
+}
+
+
+Zotero.DataObjects.prototype.getOlder = function (libraryID, date) {
+ if (!date || date.constructor.name != 'Date') {
+ throw ("date must be a JS Date in "
+ + "Zotero." + this._ZDO_Objects + ".getOlder()")
}
+ var sql = "SELECT ROWID FROM " + this._ZDO_table
+ + " WHERE libraryID=? AND clientDateModified<?";
+ return Zotero.DB.columnQuery(sql, [libraryID, Zotero.Date.dateToSQL(date, true)]);
+}
+
+
+Zotero.DataObjects.prototype.getNewer = function (libraryID, date, ignoreFutureDates) {
+ if (!date || date.constructor.name != 'Date') {
+ throw ("date must be a JS Date in "
+ + "Zotero." + this._ZDO_Objects + ".getNewer()")
+ }
- this.getIDFromLibraryAndKey = function (libraryID, key) {
- if (libraryID === null) {
- throw new Error("libraryID cannot be NULL (did you mean 0?)");
+ var sql = "SELECT ROWID FROM " + this._ZDO_table
+ + " WHERE libraryID=? AND clientDateModified>?";
+ if (ignoreFutureDates) {
+ sql += " AND clientDateModified<=CURRENT_TIMESTAMP";
+ }
+ return Zotero.DB.columnQuery(sql, [libraryID, Zotero.Date.dateToSQL(date, true)]);
+}
+
+
+/**
+ * @param {Integer} libraryID
+ * @return {Promise} A promise for an array of object ids
+ */
+Zotero.DataObjects.prototype.getUnsynced = function (libraryID) {
+ var sql = "SELECT " + this._ZDO_id + " FROM " + this._ZDO_table
+ + " WHERE libraryID=? AND synced=0";
+ return Zotero.DB.columnQueryAsync(sql, [libraryID]);
+}
+
+
+/**
+ * Get JSON from the sync cache that hasn't yet been written to the
+ * main object tables
+ *
+ * @param {Integer} libraryID
+ * @return {Promise} A promise for an array of JSON objects
+ */
+Zotero.DataObjects.prototype.getUnwrittenData = function (libraryID) {
+ var sql = "SELECT data FROM syncCache SC "
+ + "LEFT JOIN " + this._ZDO_table + " "
+ + "USING (libraryID) "
+ + "WHERE SC.libraryID=? AND "
+ + "syncObjectTypeID IN (SELECT syncObjectTypeID FROM "
+ + "syncObjectTypes WHERE name='" + this._ZDO_object + "') "
+ + "AND IFNULL(O.version, 0) < SC.version";
+ return Zotero.DB.columnQueryAsync(sql, [libraryID]);
+}
+
+
+/**
+ * Reload loaded data of loaded objects
+ *
+ * @param {Array|Number} ids - An id or array of ids
+ * @param {Array} [dataTypes] - Data types to reload (e.g., 'primaryData'), or all loaded
+ * types if not provided
+ * @param {Boolean} [reloadUnchanged=false] - Reload even data that hasn't changed internally.
+ * This should be set to true for data that was
+ * changed externally (e.g., globally renamed tags).
+ */
+Zotero.DataObjects.prototype.reload = Zotero.Promise.coroutine(function* (ids, dataTypes, reloadUnchanged) {
+ ids = Zotero.flattenArguments(ids);
+
+ Zotero.debug('Reloading ' + (dataTypes ? dataTypes + ' for ' : '')
+ + this._ZDO_objects + ' ' + ids);
+
+ for (let i=0; i<ids.length; i++) {
+ if (this._objectCache[ids[i]]) {
+ yield this._objectCache[ids[i]].reload(dataTypes, reloadUnchanged);
}
- return (this._objectIDs[libraryID] && this._objectIDs[libraryID][key])
- ? this._objectIDs[libraryID][key] : false;
}
-
- this.getOlder = function (libraryID, date) {
- if (!date || date.constructor.name != 'Date') {
- throw ("date must be a JS Date in "
- + "Zotero." + this._ZDO_Objects + ".getOlder()")
+ return true;
+});
+
+
+Zotero.DataObjects.prototype.reloadAll = function (libraryID) {
+ Zotero.debug("Reloading all " + this._ZDO_objects);
+
+ // Remove objects not stored in database
+ var sql = "SELECT ROWID FROM " + this._ZDO_table;
+ var params = [];
+ if (libraryID !== undefined) {
+ sql += ' WHERE libraryID=?';
+ params.push(libraryID);
+ }
+ return Zotero.DB.columnQueryAsync(sql, params)
+ .then(function (ids) {
+ for (var id in this._objectCache) {
+ if (!ids || ids.indexOf(parseInt(id)) == -1) {
+ delete this._objectCache[id];
+ }
}
- var sql = "SELECT ROWID FROM " + this._ZDO_table
- + " WHERE libraryID=? AND clientDateModified<?";
- return Zotero.DB.columnQuery(sql, [libraryID, Zotero.Date.dateToSQL(date, true)]);
+ // Reload data
+ this._loadedLibraries[libraryID] = false;
+ return this._load(libraryID);
+ });
+}
+
+
+Zotero.DataObjects.prototype.registerIdentifiers = function (id, libraryID, key) {
+ Zotero.debug("Registering " + this._ZDO_object + " " + id + " as " + libraryID + "/" + key);
+ if (!this._objectIDs[libraryID]) {
+ this._objectIDs[libraryID] = {};
}
-
-
- this.getNewer = function (libraryID, date, ignoreFutureDates) {
- if (!date || date.constructor.name != 'Date') {
- throw ("date must be a JS Date in "
- + "Zotero." + this._ZDO_Objects + ".getNewer()")
+ this._objectIDs[libraryID][key] = id;
+ this._objectKeys[id] = [libraryID, key];
+}
+
+
+/**
+ * Clear object from internal array
+ *
+ * @param int[] ids objectIDs
+ */
+Zotero.DataObjects.prototype.unload = function () {
+ var ids = Zotero.flattenArguments(arguments);
+ for (var i=0; i<ids.length; i++) {
+ let id = ids[i];
+ let [libraryID, key] = this.getLibraryAndKeyFromID(id);
+ if (key) {
+ delete this._objectIDs[libraryID][key];
+ delete this._objectKeys[id];
+ }
+ delete this._objectCache[id];
+ }
+}
+
+
+/**
+ * @param {Object} data1 - JSON of first object
+ * @param {Object} data2 - JSON of second object
+ * @param {Array} diff - Empty array to put diff data in
+ * @param {Boolean} [includeMatches=false] - Include all fields, even those
+ * that aren't different
+ */
+Zotero.DataObjects.prototype.diff = function (data1, data2, diff, includeMatches) {
+ diff.push({}, {});
+ var numDiffs = 0;
+
+ var skipFields = ['collectionKey', 'itemKey', 'searchKey'];
+
+ for (var field in data1) {
+ if (skipFields.indexOf(field) != -1) {
+ continue;
}
- var sql = "SELECT ROWID FROM " + this._ZDO_table
- + " WHERE libraryID=? AND clientDateModified>?";
- if (ignoreFutureDates) {
- sql += " AND clientDateModified<=CURRENT_TIMESTAMP";
+ if (data1[field] === false && (data2[field] === false || data2[field] === undefined)) {
+ continue;
}
- return Zotero.DB.columnQuery(sql, [libraryID, Zotero.Date.dateToSQL(date, true)]);
- }
-
-
- /**
- * @param {Integer} libraryID
- * @return {Promise} A promise for an array of object ids
- */
- this.getUnsynced = function (libraryID) {
- var sql = "SELECT " + this._ZDO_id + " FROM " + this._ZDO_table
- + " WHERE libraryID=? AND synced=0";
- return Zotero.DB.columnQueryAsync(sql, [libraryID]);
- }
-
-
- /**
- * Get JSON from the sync cache that hasn't yet been written to the
- * main object tables
- *
- * @param {Integer} libraryID
- * @return {Promise} A promise for an array of JSON objects
- */
- this.getUnwrittenData = function (libraryID) {
- var sql = "SELECT data FROM syncCache SC "
- + "LEFT JOIN " + this._ZDO_table + " "
- + "USING (libraryID) "
- + "WHERE SC.libraryID=? AND "
- + "syncObjectTypeID IN (SELECT syncObjectTypeID FROM "
- + "syncObjectTypes WHERE name='" + this._ZDO_object + "') "
- + "AND IFNULL(O.version, 0) < SC.version";
- return Zotero.DB.columnQueryAsync(sql, [libraryID]);
- }
-
-
- /**
- * Reload loaded data of loaded objects
- *
- * @param {Array|Number} ids - An id or array of ids
- * @param {Array} [dataTypes] - Data types to reload (e.g., 'primaryData'), or all loaded
- * types if not provided
- * @param {Boolean} [reloadUnchanged=false] - Reload even data that hasn't changed internally.
- * This should be set to true for data that was
- * changed externally (e.g., globally renamed tags).
- */
- this.reload = Zotero.Promise.coroutine(function* (ids, dataTypes, reloadUnchanged) {
- ids = Zotero.flattenArguments(ids);
- Zotero.debug('Reloading ' + (dataTypes ? dataTypes + ' for ' : '')
- + this._ZDO_objects + ' ' + ids);
+ var changed = data1[field] !== data2[field];
- for (let i=0; i<ids.length; i++) {
- if (this._objectCache[ids[i]]) {
- yield this._objectCache[ids[i]].reload(dataTypes, reloadUnchanged);
- }
+ if (includeMatches || changed) {
+ diff[0][field] = data1[field] !== false ? data1[field] : '';
+ diff[1][field] = (data2[field] !== false && data2[field] !== undefined)
+ ? data2[field] : '';
}
- return true;
- });
-
-
- this.reloadAll = function (libraryID) {
- Zotero.debug("Reloading all " + this._ZDO_objects);
-
- // Remove objects not stored in database
- var sql = "SELECT ROWID FROM " + this._ZDO_table;
- var params = [];
- if (libraryID !== undefined) {
- sql += ' WHERE libraryID=?';
- params.push(libraryID);
+ if (changed) {
+ numDiffs++;
}
- return Zotero.DB.columnQueryAsync(sql, params)
- .then(function (ids) {
- for (var id in this._objectCache) {
- if (!ids || ids.indexOf(parseInt(id)) == -1) {
- delete this._objectCache[id];
- }
- }
-
- // Reload data
- this._loadedLibraries[libraryID] = false;
- return this._load(libraryID);
- });
}
-
- this.registerIdentifiers = function (id, libraryID, key) {
- Zotero.debug("Registering " + this._ZDO_object + " " + id + " as " + libraryID + "/" + key);
- if (!this._objectIDs[libraryID]) {
- this._objectIDs[libraryID] = {};
- }
- this._objectIDs[libraryID][key] = id;
- this._objectKeys[id] = [libraryID, key];
- }
-
-
- /**
- * Clear object from internal array
- *
- * @param int[] ids objectIDs
- */
- this.unload = function () {
- var ids = Zotero.flattenArguments(arguments);
- for (var i=0; i<ids.length; i++) {
- let id = ids[i];
- let [libraryID, key] = this.getLibraryAndKeyFromID(id);
- if (key) {
- delete this._objectIDs[libraryID][key];
- delete this._objectKeys[id];
- }
- delete this._objectCache[id];
+ // DEBUG: some of this is probably redundant
+ for (var field in data2) {
+ if (skipFields.indexOf(field) != -1) {
+ continue;
}
- }
-
-
- /**
- * @param {Object} data1 - JSON of first object
- * @param {Object} data2 - JSON of second object
- * @param {Array} diff - Empty array to put diff data in
- * @param {Boolean} [includeMatches=false] - Include all fields, even those
- * that aren't different
- */
- this.diff = function (data1, data2, diff, includeMatches) {
- diff.push({}, {});
- var numDiffs = 0;
- var skipFields = ['collectionKey', 'itemKey', 'searchKey'];
+ if (diff[0][field] !== undefined) {
+ continue;
+ }
- for (var field in data1) {
- if (skipFields.indexOf(field) != -1) {
- continue;
- }
-
- if (data1[field] === false && (data2[field] === false || data2[field] === undefined)) {
- continue;
- }
-
- var changed = data1[field] !== data2[field];
-
- if (includeMatches || changed) {
- diff[0][field] = data1[field] !== false ? data1[field] : '';
- diff[1][field] = (data2[field] !== false && data2[field] !== undefined)
- ? data2[field] : '';
- }
-
- if (changed) {
- numDiffs++;
- }
+ if (data2[field] === false && (data1[field] === false || data1[field] === undefined)) {
+ continue;
}
- // DEBUG: some of this is probably redundant
- for (var field in data2) {
- if (skipFields.indexOf(field) != -1) {
- continue;
- }
-
- if (diff[0][field] !== undefined) {
- continue;
- }
-
- if (data2[field] === false && (data1[field] === false || data1[field] === undefined)) {
- continue;
- }
-
- var changed = data1[field] !== data2[field];
-
- if (includeMatches || changed) {
- diff[0][field] = (data1[field] !== false && data1[field] !== undefined)
- ? data1[field] : '';
- diff[1][field] = data2[field] !== false ? data2[field] : '';
- }
-
- if (changed) {
- numDiffs++;
- }
+ var changed = data1[field] !== data2[field];
+
+ if (includeMatches || changed) {
+ diff[0][field] = (data1[field] !== false && data1[field] !== undefined)
+ ? data1[field] : '';
+ diff[1][field] = data2[field] !== false ? data2[field] : '';
}
- return numDiffs;
+ if (changed) {
+ numDiffs++;
+ }
}
+ return numDiffs;
+}
+
+
+Zotero.DataObjects.prototype.isEditable = function (obj) {
+ var libraryID = obj.libraryID;
+ if (!libraryID) {
+ return true;
+ }
- this.isEditable = function (obj) {
- var libraryID = obj.libraryID;
- if (!libraryID) {
- return true;
- }
- var type = Zotero.Libraries.getType(libraryID);
- switch (type) {
- case 'user':
- return true;
-
- case 'group':
- var groupID = Zotero.Groups.getGroupIDFromLibraryID(libraryID);
- var group = Zotero.Groups.get(groupID);
- if (!group.editable) {
- return false;
- }
- if (obj.objectType == 'item' && obj.isAttachment()
- && (obj.attachmentLinkMode == Zotero.Attachments.LINK_MODE_IMPORTED_URL ||
- obj.attachmentLinkMode == Zotero.Attachments.LINK_MODE_IMPORTED_FILE)) {
- return group.filesEditable;
- }
- return true;
-
- default:
- throw ("Unsupported library type '" + type + "' in Zotero.DataObjects.isEditable()");
- }
+ if (!Zotero.Libraries.isEditable(libraryID)) return false;
+
+ if (obj.objectType == 'item' && obj.isAttachment()
+ && (obj.attachmentLinkMode == Zotero.Attachments.LINK_MODE_IMPORTED_URL ||
+ obj.attachmentLinkMode == Zotero.Attachments.LINK_MODE_IMPORTED_FILE)
+ && !Zotero.Libraries.isFilesEditable(libraryID)
+ ) {
+ return false;
}
+ return true;
+}
+
+
+Zotero.DataObjects.prototype.editCheck = function (obj) {
+ if (!Zotero.Sync.Server.updatesInProgress && !Zotero.Sync.Storage.updatesInProgress && !this.isEditable(obj)) {
+ throw ("Cannot edit " + this._ZDO_object + " in read-only Zotero library");
+ }
+}
+
+Zotero.defineProperty(Zotero.DataObjects.prototype, "primaryDataSQL", {
+ get: function () {
+ return "SELECT "
+ + Object.keys(this._primaryDataSQLParts).map((val) => this._primaryDataSQLParts[val]).join(', ')
+ + this.primaryDataSQLFrom;
+ }
+}, {lazy: true});
+
+Zotero.DataObjects.prototype._primaryDataSQLWhere = "WHERE 1";
+
+Zotero.DataObjects.prototype.getPrimaryDataSQLPart = function (part) {
+ var sql = this._primaryDataSQLParts[part];
+ if (!sql) {
+ throw new Error("Invalid primary data SQL part '" + part + "'");
+ }
+ return sql;
+}
+
+
+Zotero.DataObjects.prototype._load = Zotero.Promise.coroutine(function* (libraryID, ids, options) {
+ var loaded = {};
- this.editCheck = function (obj) {
- if (!Zotero.Sync.Server.updatesInProgress && !Zotero.Sync.Storage.updatesInProgress && !this.isEditable(obj)) {
- throw ("Cannot edit " + this._ZDO_object + " in read-only Zotero library");
- }
+ // If library isn't an integer (presumably false or null), skip it
+ if (parseInt(libraryID) != libraryID) {
+ libraryID = false;
}
+ if (libraryID === false && !ids) {
+ throw new Error("Either libraryID or ids must be provided");
+ }
- this.getPrimaryDataSQLPart = function (part) {
- var sql = this._primaryDataSQLParts[part];
- if (!sql) {
- throw new Error("Invalid primary data SQL part '" + part + "'");
- }
- return sql;
+ if (libraryID !== false && this._loadedLibraries[libraryID]) {
+ return loaded;
}
+ // getPrimaryDataSQL() should use "O" for the primary table alias
+ var sql = this.primaryDataSQL;
+ var params = [];
+ if (libraryID !== false) {
+ sql += ' AND O.libraryID=?';
+ params.push(libraryID);
+ }
+ if (ids) {
+ sql += ' AND O.' + this._ZDO_id + ' IN (' + ids.join(',') + ')';
+ }
- this._load = Zotero.Promise.coroutine(function* (libraryID, ids, options) {
- var loaded = {};
-
- // If library isn't an integer (presumably false or null), skip it
- if (parseInt(libraryID) != libraryID) {
- libraryID = false;
- }
-
- if (libraryID === false && !ids) {
- throw new Error("Either libraryID or ids must be provided");
- }
-
- if (libraryID !== false && this._loadedLibraries[libraryID]) {
- return loaded;
- }
-
- // getPrimaryDataSQL() should use "O" for the primary table alias
- var sql = this.getPrimaryDataSQL();
- var params = [];
- if (libraryID !== false) {
- sql += ' AND O.libraryID=?';
- params.push(libraryID);
- }
- if (ids) {
- sql += ' AND O.' + this._ZDO_id + ' IN (' + ids.join(',') + ')';
- }
-
- var t = new Date();
- yield Zotero.DB.queryAsync(
- sql,
- params,
- {
- onRow: function (row) {
- var id = row.getResultByIndex(this._ZDO_id);
- var columns = Object.keys(this._primaryDataSQLParts);
- var rowObj = {};
- for (let i=0; i<columns.length; i++) {
- rowObj[columns[i]] = row.getResultByIndex(i);
- }
- var obj;
-
- // Existing object -- reload in place
- if (this._objectCache[id]) {
- this._objectCache[id].loadFromRow(rowObj, true);
- obj = this._objectCache[id];
- }
- // Object doesn't exist -- create new object and stuff in cache
- else {
- obj = new Zotero[this._ZDO_Object];
- obj.loadFromRow(rowObj, true);
- if (!options || !options.noCache) {
- this._objectCache[id] = obj;
- }
- }
- loaded[id] = obj;
- }.bind(this)
- }
- );
- Zotero.debug("Loaded " + this._ZDO_objects + " in " + ((new Date) - t) + "ms");
-
- if (!ids) {
- this._loadedLibraries[libraryID] = true;
-
- // If loading all objects, remove cached objects that no longer exist
- for (let i in this._objectCache) {
- let obj = this._objectCache[i];
- if (libraryID !== false && obj.libraryID !== libraryID) {
- continue;
+ var t = new Date();
+ yield Zotero.DB.queryAsync(
+ sql,
+ params,
+ {
+ onRow: function (row) {
+ var id = row.getResultByIndex(this._ZDO_id);
+ var columns = Object.keys(this._primaryDataSQLParts);
+ var rowObj = {};
+ for (let i=0; i<columns.length; i++) {
+ rowObj[columns[i]] = row.getResultByIndex(i);
}
- if (!loaded[obj.id]) {
- this.unload(obj.id);
+ var obj;
+
+ // Existing object -- reload in place
+ if (this._objectCache[id]) {
+ this._objectCache[id].loadFromRow(rowObj, true);
+ obj = this._objectCache[id];
}
+ // Object doesn't exist -- create new object and stuff in cache
+ else {
+ obj = new Zotero[this._ZDO_Object];
+ obj.loadFromRow(rowObj, true);
+ if (!options || !options.noCache) {
+ this._objectCache[id] = obj;
+ }
+ }
+ loaded[id] = obj;
+ }.bind(this)
+ }
+ );
+ Zotero.debug("Loaded " + this._ZDO_objects + " in " + ((new Date) - t) + "ms");
+
+ if (!ids) {
+ this._loadedLibraries[libraryID] = true;
+
+ // If loading all objects, remove cached objects that no longer exist
+ for (let i in this._objectCache) {
+ let obj = this._objectCache[i];
+ if (libraryID !== false && obj.libraryID !== libraryID) {
+ continue;
}
-
- if (this._postLoad) {
- this._postLoad(libraryID, ids);
+ if (!loaded[obj.id]) {
+ this.unload(obj.id);
}
}
- return loaded;
- });
-
-
- this._loadIDsAndKeys = Zotero.Promise.coroutine(function* () {
- var sql = "SELECT ROWID AS id, libraryID, key FROM " + this._ZDO_table;
- var rows = yield Zotero.DB.queryAsync(sql);
- for (let i=0; i<rows.length; i++) {
- let row = rows[i];
- this._objectKeys[row.id] = [row.libraryID, row.key];
- if (!this._objectIDs[row.libraryID]) {
- this._objectIDs[row.libraryID] = {};
- }
- this._objectIDs[row.libraryID][row.key] = row.id;
+ if (this._postLoad) {
+ this._postLoad(libraryID, ids);
}
- });
-}
+ }
+
+ return loaded;
+});
+
+
+Zotero.DataObjects.prototype._loadIDsAndKeys = Zotero.Promise.coroutine(function* () {
+ var sql = "SELECT ROWID AS id, libraryID, key FROM " + this._ZDO_table;
+ var rows = yield Zotero.DB.queryAsync(sql);
+ for (let i=0; i<rows.length; i++) {
+ let row = rows[i];
+ this._objectKeys[row.id] = [row.libraryID, row.key];
+ if (!this._objectIDs[row.libraryID]) {
+ this._objectIDs[row.libraryID] = {};
+ }
+ this._objectIDs[row.libraryID][row.key] = row.id;
+ }
+});
diff --git a/chrome/content/zotero/xpcom/data/items.js b/chrome/content/zotero/xpcom/data/items.js
@@ -27,17 +27,16 @@
/*
* Primary interface for accessing Zotero items
*/
-Zotero.Items = new function() {
- Zotero.DataObjects.apply(this, ['item']);
- this.constructor.prototype = new Zotero.DataObjects();
+Zotero.Items = function() {
+ this.constructor = null;
- // Privileged methods
- this.add = add;
- this.getSortTitle = getSortTitle;
+ this._ZDO_object = 'item';
- Object.defineProperty(this, "_primaryDataSQLParts", {
+ // This needs to wait until all Zotero components are loaded to initialize,
+ // but otherwise it can be just a simple property
+ Zotero.defineProperty(this, "_primaryDataSQLParts", {
get: function () {
- return _primaryDataSQLParts ? _primaryDataSQLParts : (_primaryDataSQLParts = {
+ return {
itemID: "O.itemID",
itemTypeID: "O.itemTypeID",
dateAdded: "O.dateAdded",
@@ -88,18 +87,17 @@ Zotero.Items = new function() {
attachmentContentType: "IA.contentType AS attachmentContentType",
attachmentPath: "IA.path AS attachmentPath",
attachmentSyncState: "IA.syncState AS attachmentSyncState"
- });
+ };
}
- });
+ }, {lazy: true});
- // Private members
- var _primaryDataSQLParts;
- var _cachedFields = {};
- var _firstCreatorSQL = '';
- var _sortCreatorSQL = '';
- var _emptyTrashIdleObserver = null;
- var _emptyTrashTimer = null;
+ this._primaryDataSQLFrom = "FROM items O "
+ + "LEFT JOIN itemAttachments IA USING (itemID) "
+ + "LEFT JOIN items IAP ON (IA.parentItemID=IAP.itemID) "
+ + "LEFT JOIN itemNotes INo ON (O.itemID=INo.itemID) "
+ + "LEFT JOIN items INoP ON (INo.parentItemID=INoP.itemID) "
+ + "LEFT JOIN deletedItems DI ON (O.itemID=DI.itemID)";
/**
* Return items marked as deleted
@@ -215,8 +213,7 @@ Zotero.Items = new function() {
};
-
-
+ this._cachedFields = {};
this.cacheFields = Zotero.Promise.coroutine(function* (libraryID, fields, items) {
if (items && items.length == 0) {
return;
@@ -246,14 +243,14 @@ Zotero.Items = new function() {
var fieldIDs = [];
for each(var field in fields) {
// Check if field already cached
- if (_cachedFields[libraryID] && _cachedFields[libraryID].indexOf(field) != -1) {
+ if (this._cachedFields[libraryID] && this._cachedFields[libraryID].indexOf(field) != -1) {
continue;
}
- if (!_cachedFields[libraryID]) {
- _cachedFields[libraryID] = [];
+ if (!this._cachedFields[libraryID]) {
+ this._cachedFields[libraryID] = [];
}
- _cachedFields[libraryID].push(field);
+ this._cachedFields[libraryID].push(field);
if (this.isPrimaryField(field)) {
primaryFields.push(field);
@@ -403,7 +400,7 @@ Zotero.Items = new function() {
for (let i=0; i<allItemIDs.length; i++) {
let itemID = allItemIDs[i];
let item = this._objectCache[itemID];
- yield this._objectCache[itemID].loadDisplayTitle()
+ yield item.loadDisplayTitle()
}
}
@@ -428,7 +425,7 @@ Zotero.Items = new function() {
// Move child items to master
var ids = otherItem.getAttachments(true).concat(otherItem.getNotes(true));
for each(var id in ids) {
- var attachment = yield Zotero.Items.getAsync(id);
+ var attachment = yield this.getAsync(id);
// TODO: Skip identical children?
@@ -480,7 +477,7 @@ Zotero.Items = new function() {
}
yield item.save();
- });
+ }.bind(this));
};
@@ -535,9 +532,11 @@ Zotero.Items = new function() {
/**
* Start idle observer to delete trashed items older than a certain number of days
*/
+ this._emptyTrashIdleObserver = null;
+ this._emptyTrashTimer = null;
this.startEmptyTrashTimer = function () {
- _emptyTrashIdleObserver = {
- observe: function (subject, topic, data) {
+ this._emptyTrashIdleObserver = {
+ observe: (subject, topic, data) => {
if (topic == 'idle' || topic == 'timer-callback') {
var days = Zotero.Prefs.get('trashAutoEmptyDays');
if (!days) {
@@ -551,20 +550,20 @@ Zotero.Items = new function() {
// TODO: increase number after dealing with slow
// tag.getLinkedItems() call during deletes
var num = 10;
- Zotero.Items.emptyTrash(null, days, num)
- .then(function (deleted) {
+ this.emptyTrash(null, days, num)
+ .then(deleted => {
if (!deleted) {
- _emptyTrashTimer = null;
+ this._emptyTrashTimer = null;
return;
}
// Set a timer to do more every few seconds
- if (!_emptyTrashTimer) {
- _emptyTrashTimer = Components.classes["@mozilla.org/timer;1"]
+ if (!this._emptyTrashTimer) {
+ this._emptyTrashTimer = Components.classes["@mozilla.org/timer;1"]
.createInstance(Components.interfaces.nsITimer);
}
- _emptyTrashTimer.init(
- _emptyTrashIdleObserver.observe,
+ this._emptyTrashTimer.init(
+ this._emptyTrashIdleObserver.observe,
5 * 1000,
Components.interfaces.nsITimer.TYPE_ONE_SHOT
);
@@ -572,8 +571,8 @@ Zotero.Items = new function() {
}
// When no longer idle, cancel timer
else if (topic == 'back') {
- if (_emptyTrashTimer) {
- _emptyTrashTimer.cancel();
+ if (this._emptyTrashTimer) {
+ this._emptyTrashTimer.cancel();
}
}
}
@@ -581,7 +580,7 @@ Zotero.Items = new function() {
var idleService = Components.classes["@mozilla.org/widget/idleservice;1"].
getService(Components.interfaces.nsIIdleService);
- idleService.addIdleObserver(_emptyTrashIdleObserver, 305);
+ idleService.addIdleObserver(this._emptyTrashIdleObserver, 305);
}
@@ -624,28 +623,12 @@ Zotero.Items = new function() {
});
- this.getPrimaryDataSQL = function () {
- return "SELECT "
- + Object.keys(this._primaryDataSQLParts).map((val) => this._primaryDataSQLParts[val]).join(', ')
- + this.primaryDataSQLFrom;
- };
-
-
- this.primaryDataSQLFrom = " FROM items O "
- + "LEFT JOIN itemAttachments IA USING (itemID) "
- + "LEFT JOIN items IAP ON (IA.parentItemID=IAP.itemID) "
- + "LEFT JOIN itemNotes INo ON (O.itemID=INo.itemID) "
- + "LEFT JOIN items INoP ON (INo.parentItemID=INoP.itemID) "
- + "LEFT JOIN deletedItems DI ON (O.itemID=DI.itemID) "
- + "WHERE 1";
-
-
this._postLoad = function (libraryID, ids) {
if (!ids) {
- if (!_cachedFields[libraryID]) {
- _cachedFields[libraryID] = [];
+ if (!this._cachedFields[libraryID]) {
+ this._cachedFields[libraryID] = [];
}
- _cachedFields[libraryID] = this.primaryFields.concat();
+ this._cachedFields[libraryID] = this.primaryFields.concat();
}
}
@@ -655,6 +638,7 @@ Zotero.Items = new function() {
*
* Why do we do this entirely in SQL? Because we're crazy. Crazy like foxes.
*/
+ var _firstCreatorSQL = '';
function _getFirstCreatorSQL() {
if (_firstCreatorSQL) {
return _firstCreatorSQL;
@@ -759,6 +743,7 @@ Zotero.Items = new function() {
/*
* Generate SQL to retrieve sortCreator field
*/
+ var _sortCreatorSQL = '';
function _getSortCreatorSQL() {
if (_sortCreatorSQL) {
return _sortCreatorSQL;
@@ -878,7 +863,7 @@ Zotero.Items = new function() {
}
- function getSortTitle(title) {
+ this.getSortTitle = function(title) {
if (title === false || title === undefined) {
return '';
}
@@ -887,4 +872,8 @@ Zotero.Items = new function() {
}
return title.replace(/^[\[\'\"](.*)[\'\"\]]?$/, '$1')
}
-}
+
+ Zotero.DataObjects.call(this);
+
+ return this;
+}.bind(Object.create(Zotero.DataObjects.prototype))();
diff --git a/chrome/content/zotero/xpcom/data/relations.js b/chrome/content/zotero/xpcom/data/relations.js
@@ -23,18 +23,15 @@
***** END LICENSE BLOCK *****
*/
-Zotero.Relations = new function () {
- Zotero.DataObjects.apply(this, ['relation']);
- this.constructor.prototype = new Zotero.DataObjects();
+Zotero.Relations = function () {
+ this.constructor = null;
- this.__defineGetter__('relatedItemPredicate', function () "dc:relation");
- this.__defineGetter__('linkedObjectPredicate', function () "owl:sameAs");
- this.__defineGetter__('deletedItemPredicate', function () 'dc:isReplacedBy');
+ this._ZDO_object = 'relation';
+ this._ZDO_idOnly = true;
- var _namespaces = {
- dc: 'http://purl.org/dc/elements/1.1/',
- owl: 'http://www.w3.org/2002/07/owl#'
- };
+ Zotero.defineProperty(this, 'relatedItemPredicate', {value: 'dc:relation'});
+ Zotero.defineProperty(this, 'linkedObjectPredicate', {value: 'owl:sameAs'});
+ Zotero.defineProperty(this, 'deletedItemPredicate', {value: 'dc:isReplacedBy'});
this.get = function (id) {
if (typeof id != 'number') {
@@ -52,7 +49,7 @@ Zotero.Relations = new function () {
*/
this.getByURIs = Zotero.Promise.coroutine(function* (subject, predicate, object) {
if (predicate) {
- predicate = _getPrefixAndValue(predicate).join(':');
+ predicate = this._getPrefixAndValue(predicate).join(':');
}
if (!subject && !predicate && !object) {
@@ -141,7 +138,7 @@ Zotero.Relations = new function () {
this.add = Zotero.Promise.coroutine(function* (libraryID, subject, predicate, object) {
- predicate = _getPrefixAndValue(predicate).join(':');
+ predicate = this._getPrefixAndValue(predicate).join(':');
var relation = new Zotero.Relation;
if (!libraryID) {
@@ -272,11 +269,15 @@ Zotero.Relations = new function () {
return relation;
}
+ this._namespaces = {
+ dc: 'http://purl.org/dc/elements/1.1/',
+ owl: 'http://www.w3.org/2002/07/owl#'
+ };
- function _getPrefixAndValue(uri) {
+ this._getPrefixAndValue = function(uri) {
var [prefix, value] = uri.split(':');
if (prefix && value) {
- if (!_namespaces[prefix]) {
+ if (!this._namespaces[prefix]) {
throw ("Invalid prefix '" + prefix + "' in Zotero.Relations._getPrefixAndValue()");
}
return [prefix, value];
@@ -290,4 +291,8 @@ Zotero.Relations = new function () {
}
throw ("Invalid namespace in URI '" + uri + "' in Zotero.Relations._getPrefixAndValue()");
}
-}
+
+ Zotero.DataObjects.call(this);
+
+ return this;
+}.bind(Object.create(Zotero.DataObjects.prototype))();
+\ No newline at end of file
diff --git a/chrome/content/zotero/xpcom/search.js b/chrome/content/zotero/xpcom/search.js
@@ -1637,29 +1637,25 @@ Zotero.Search.prototype._buildQuery = Zotero.Promise.coroutine(function* () {
this._sqlParams = sqlParams.length ? sqlParams : false;
});
-Zotero.Searches = new function(){
- Zotero.DataObjects.apply(this, ['search', 'searches', 'savedSearch', 'savedSearches']);
- this.constructor.prototype = new Zotero.DataObjects();
-
- Object.defineProperty(this, "_primaryDataSQLParts", {
- get: function () {
- return _primaryDataSQLParts ? _primaryDataSQLParts : (_primaryDataSQLParts = {
- savedSearchID: "O.savedSearchID",
- name: "O.savedSearchName",
- libraryID: "O.libraryID",
- key: "O.key",
- version: "O.version",
- synced: "O.synced"
- });
- }
- });
-
-
- var _primaryDataSQLParts;
+Zotero.Searches = function() {
+ this.constructor = null;
+
+ this._ZDO_object = 'search';
+ this._ZDO_id = 'savedSearch';
+ this._ZDO_table = 'savedSearches';
+
+ this._primaryDataSQLParts = {
+ savedSearchID: "O.savedSearchID",
+ name: "O.savedSearchName",
+ libraryID: "O.libraryID",
+ key: "O.key",
+ version: "O.version",
+ synced: "O.synced"
+ }
this.init = Zotero.Promise.coroutine(function* () {
- yield this.constructor.prototype.init.apply(this);
+ yield Zotero.DataObjects.prototype.init.apply(this);
yield Zotero.SearchConditions.init();
});
@@ -1730,7 +1726,11 @@ Zotero.Searches = new function(){
+ Object.keys(this._primaryDataSQLParts).map(key => this._primaryDataSQLParts[key]).join(", ") + " "
+ "FROM savedSearches O WHERE 1";
}
-}
+
+ Zotero.DataObjects.call(this);
+
+ return this;
+}.bind(Object.create(Zotero.DataObjects.prototype))();