libzotero
Version:
javascript libZotero
251 lines (228 loc) • 7.79 kB
JavaScript
;
var log = require('./Log.js').Logger('libZotero:Container');
module.exports = function () {};
module.exports.prototype.initSecondaryData = function () {};
module.exports.prototype.addObject = function (object) {
log.debug('Zotero.Container.addObject', 4);
var container = this;
container.objectArray.push(object);
container.objectMap[object.key] = object;
if (container.owningLibrary) {
object.associateWithLibrary(container.owningLibrary);
}
return container;
};
module.exports.prototype.fieldComparer = function (field) {
if (Intl) {
var collator = new Intl.Collator();
return function (a, b) {
return collator.compare(a.apiObj.data[field], b.apiObj.data[field]);
};
} else {
return function (a, b) {
if (a.apiObj.data[field].toLowerCase() == b.apiObj.data[field].toLowerCase()) {
return 0;
}
if (a.apiObj.data[field].toLowerCase() < b.apiObj.data[field].toLowerCase()) {
return -1;
}
return 1;
};
}
};
module.exports.prototype.getObject = function (key) {
var container = this;
if (container.objectMap.hasOwnProperty(key)) {
return container.objectMap[key];
} else {
return false;
}
};
module.exports.prototype.getObjects = function (keys) {
var container = this;
var objects = [];
var object;
for (var i = 0; i < keys.length; i++) {
object = container.getObject(keys[i]);
if (object) {
objects.push(object);
}
}
return objects;
};
module.exports.prototype.removeObject = function (key) {
var container = this;
if (container.objectMap.hasOwnProperty(key)) {
delete container.objectmap[key];
container.initSecondaryData();
}
};
module.exports.prototype.removeObjects = function (keys) {
var container = this;
//delete Objects from objectMap;
for (var i = 0; i < keys.length; i++) {
delete container.objectMap[keys[i]];
}
//rebuild array
container.initSecondaryData();
};
module.exports.prototype.writeObjects = function (objects) {
//TODO:implement
};
//generate keys for objects about to be written if they are new
module.exports.prototype.assignKeys = function (objectsArray) {
var object;
for (var i = 0; i < objectsArray.length; i++) {
object = objectsArray[i];
var key = object.get('key');
if (!key) {
var newObjectKey = Zotero.utils.getKey();
object.set('key', newObjectKey);
object.set('version', 0);
}
}
return objectsArray;
};
//split an array of objects into chunks to write over multiple api requests
module.exports.prototype.chunkObjectsArray = function (objectsArray) {
var chunkSize = 50;
var writeChunks = [];
for (var i = 0; i < objectsArray.length; i = i + chunkSize) {
writeChunks.push(objectsArray.slice(i, i + chunkSize));
}
return writeChunks;
};
module.exports.prototype.rawChunks = function (chunks) {
var rawChunkObjects = [];
for (var i = 0; i < chunks.length; i++) {
rawChunkObjects[i] = [];
for (var j = 0; j < chunks[i].length; j++) {
rawChunkObjects[i].push(chunks[i][j].writeApiObj());
}
}
return rawChunkObjects;
};
/**
* Update syncState property on container to keep track of updates that occur during sync process.
* Set earliestVersion to MIN(earliestVersion, version).
* Set latestVersion to MAX(latestVersion, version).
* This should be called with the modifiedVersion header for each response tied to this container
* during a sync process.
* @param {int} version
* @return {null}
*/
module.exports.prototype.updateSyncState = function (version) {
var container = this;
log.debug('updateSyncState: ' + version, 3);
if (!container.hasOwnProperty('syncState')) {
log.debug('no syncState property');
throw new Error('Attempt to update sync state of object with no syncState property');
}
if (container.syncState.earliestVersion === null) {
container.syncState.earliestVersion = version;
}
if (container.syncState.latestVersion === null) {
container.syncState.latestVersion = version;
}
if (version < container.syncState.earliestVersion) {
container.syncState.earliestVersion = version;
}
if (version > container.syncState.latestVersion) {
container.syncState.latestVersion = version;
}
log.debug('done updating sync state', 3);
};
module.exports.prototype.updateSyncedVersion = function (versionField) {
var container = this;
if (container.syncState.earliestVersion !== null && container.syncState.earliestVersion == container.syncState.latestVersion) {
container.version = container.syncState.latestVersion;
container.synced = true;
} else if (container.syncState.earliestVersion !== null) {
container.version = container.syncState.earliestVersion;
}
};
module.exports.prototype.processDeletions = function (deletedKeys) {
var container = this;
for (var i = 0; i < deletedKeys.length; i++) {
var localObject = container.get(deletedKeys[i]);
if (localObject !== false) {
//still have object locally
if (localObject.synced === true) {
//our object is not modified, so delete it as the server thinks we should
container.removeObjects([deletedKeys[i]]);
} else {
//TODO: conflict resolution
}
}
}
};
//update items appropriately based on response to multi-write request
//for success:
// update objectKey if item doesn't have one yet (newly created item)
// update itemVersion to response's Last-Modified-Version header
// mark as synced
//for unchanged:
// don't need to do anything? itemVersion should remain the same?
// mark as synced if not already?
//for failed:
// add the failure to the object under writeFailure
// don't mark as synced
// calling code should check for writeFailure after the written objects
// are returned
module.exports.prototype.updateObjectsFromWriteResponse = function (objectsArray, response) {
log.debug('Zotero.Container.updateObjectsFromWriteResponse', 3);
log.debug('statusCode: ' + response.status, 3);
return new Promise(function (resolve, reject) {
if (response.status == 200) {
response.json().then(function (data) {
var lastModifiedVersion = response.headers.get('Last-Modified-Version');
log.debug('newLastModifiedVersion: ' + lastModifiedVersion, 3);
//make sure writes were actually successful and
//update the itemKey for the parent
if (data.hasOwnProperty('success') && Object.keys(data.success).length) {
//update each successfully written item, possibly with new itemKeys
Object.keys(data.success).forEach(function (ind) {
var i = parseInt(ind, 10);
var key = data.success[ind];
var object = objectsArray[i];
//throw error if objectKey mismatch
if (object.key !== '' && object.key !== key) {
throw new Error('object key mismatch in multi-write response');
}
if (object.key === '') {
object.updateObjectKey(key);
}
object.set('version', lastModifiedVersion);
object.synced = true;
object.writeFailure = false;
});
resolve();
} else if (data.hasOwnProperty('failed') && Object.keys(data.failed).length) {
log.debug('updating objects with failed writes', 3);
Object.keys(data.failed).forEach(function (ind) {
var failure = data.failed[ind];
log.error('failed write ' + ind + ' - ' + failure);
var i = parseInt(ind, 10);
var object = objectsArray[i];
object.writeFailure = failure;
});
reject();
} else {
resolve();
}
});
} else if (response.status == 204) {
//single item put response, this probably should never go to this function
objectsArray[0].synced = true;
resolve();
}
});
};
//return the key as a string when passed an argument that
//could be either a string key or an object with a key property
module.exports.prototype.extractKey = function (object) {
if (typeof object == 'string') {
return object;
}
return object.get('key');
};