Add Zotero Commons feature (integration between Zotero and Internet Archive).
- dropping Zotero items into a bucket puts them in that IA bucket - double clicking a bucket takes you to that IA bucket In order to enable Zotero Commons: 1) Get an access key and secret key at http://www.archive.org/account/s3.php 2) Go to about:config 3) Search "commons" (no quotes) 4) Set "extensions.zotero.commons.enabled" to true 5) Enter your S3 access key into "extensions.zotero.commons.accessKey" 6) Enter your S3 secret key into "extensions.zotero.commons.secretKey" 7) Enter your buckets into "extensions.zotero.commons.buckets" as a comma separated list Note: Steps 4-7 take effect in new windows
This commit is contained in:
parent
bb3c2ff55d
commit
ac79b1d05f
9 changed files with 636 additions and 6 deletions
|
@ -1983,8 +1983,17 @@ var ZoteroPane = new function()
|
|||
ZoteroPane.loadURI(uri);
|
||||
event.stopPropagation();
|
||||
}
|
||||
else if(itemGroup.ref.id == 'commons-header') {
|
||||
ZoteroPane.loadURI(Zotero.Commons.uri);
|
||||
event.stopPropagation();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (itemGroup.isBucket()) {
|
||||
ZoteroPane.loadURI(itemGroup.ref.uri);
|
||||
event.stopPropagation();
|
||||
}
|
||||
}
|
||||
else if (tree.id == 'zotero-items-tree') {
|
||||
var viewOnDoubleClick = Zotero.Prefs.get('viewOnDoubleClick');
|
||||
|
|
|
@ -224,6 +224,26 @@ Zotero.CollectionTreeView.prototype.refresh = function()
|
|||
this._showItem(new Zotero.ItemGroup('share', share));
|
||||
}
|
||||
}
|
||||
|
||||
var buckets = Zotero.Commons.buckets;
|
||||
if(buckets.length) {
|
||||
this._showItem(new Zotero.ItemGroup('separator', false));
|
||||
var header = {
|
||||
id: "commons-header",
|
||||
label: "Commons", // TODO: localize
|
||||
expand: function (buckets) {
|
||||
if (!buckets) {
|
||||
var buckets = Zotero.Commons.buckets;
|
||||
}
|
||||
|
||||
for(var i = 0, len = buckets.length; i < len; i++) {
|
||||
self._showItem(new Zotero.ItemGroup('bucket', buckets[i]), 1);
|
||||
}
|
||||
}
|
||||
};
|
||||
this._showItem(new Zotero.ItemGroup('header', header), null, null, true);
|
||||
header.expand(buckets);
|
||||
}
|
||||
|
||||
this._refreshHashMap();
|
||||
|
||||
|
@ -473,6 +493,9 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col)
|
|||
if (source.ref.id == 'group-libraries-header') {
|
||||
collectionType = 'groups';
|
||||
}
|
||||
else if (source.ref.id == 'commons-header') {
|
||||
collectionType = 'commons';
|
||||
}
|
||||
break;
|
||||
|
||||
case 'group':
|
||||
|
@ -485,7 +508,7 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col)
|
|||
Zotero.CollectionTreeView.prototype.isContainer = function(row)
|
||||
{
|
||||
var itemGroup = this._getItemAtRow(row);
|
||||
return itemGroup.isLibrary(true) || itemGroup.isCollection() || itemGroup.isHeader();
|
||||
return itemGroup.isLibrary(true) || itemGroup.isCollection() || itemGroup.isHeader() || itemGroup.isBucket();
|
||||
}
|
||||
|
||||
Zotero.CollectionTreeView.prototype.isContainerOpen = function(row)
|
||||
|
@ -505,6 +528,9 @@ Zotero.CollectionTreeView.prototype.isContainerEmpty = function(row)
|
|||
if (itemGroup.isHeader()) {
|
||||
return false;
|
||||
}
|
||||
if (itemGroup.isBucket()) {
|
||||
return true;
|
||||
}
|
||||
if (itemGroup.isGroup()) {
|
||||
return !itemGroup.ref.hasCollections();
|
||||
}
|
||||
|
@ -562,6 +588,8 @@ Zotero.CollectionTreeView.prototype.toggleOpenState = function(row)
|
|||
if (itemGroup.type == 'header') {
|
||||
itemGroup.ref.expand();
|
||||
}
|
||||
else if(itemGroup.type == 'bucket') {
|
||||
}
|
||||
else {
|
||||
if (itemGroup.isLibrary()) {
|
||||
count = itemGroup.ref.expand();
|
||||
|
@ -999,6 +1027,11 @@ Zotero.CollectionTreeView.prototype.canDrop = function(row, orient, dragData)
|
|||
}
|
||||
|
||||
if (dataType == 'zotero/item') {
|
||||
|
||||
if(itemGroup.isBucket()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
var ids = data;
|
||||
var items = Zotero.Items.get(ids);
|
||||
var skip = true;
|
||||
|
@ -1139,6 +1172,11 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient)
|
|||
else {
|
||||
var targetLibraryID = null;
|
||||
}
|
||||
|
||||
if(itemGroup.isBucket()) {
|
||||
itemGroup.ref.uploadItems(ids);
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.DB.beginTransaction();
|
||||
|
||||
|
@ -1472,6 +1510,11 @@ Zotero.ItemGroup.prototype.isShare = function()
|
|||
return this.type == 'share';
|
||||
}
|
||||
|
||||
Zotero.ItemGroup.prototype.isBucket = function()
|
||||
{
|
||||
return this.type == 'bucket';
|
||||
}
|
||||
|
||||
Zotero.ItemGroup.prototype.isTrash = function()
|
||||
{
|
||||
return this.type == 'trash';
|
||||
|
@ -1535,6 +1578,9 @@ Zotero.ItemGroup.prototype.getName = function()
|
|||
|
||||
case 'share':
|
||||
return this.ref.name;
|
||||
|
||||
case 'bucket':
|
||||
return this.ref.name;
|
||||
|
||||
case 'trash':
|
||||
return Zotero.getString('pane.collections.trash');
|
||||
|
@ -1556,6 +1602,9 @@ Zotero.ItemGroup.prototype.getChildItems = function()
|
|||
// Fake results if this is a shared library
|
||||
case 'share':
|
||||
return this.ref.getAll();
|
||||
|
||||
case 'bucket':
|
||||
return this.ref.getItems();
|
||||
|
||||
case 'header':
|
||||
return [];
|
||||
|
@ -1661,6 +1710,9 @@ Zotero.ItemGroup.prototype.getChildTags = function() {
|
|||
// TODO: implement?
|
||||
case 'share':
|
||||
return false;
|
||||
|
||||
case 'bucket':
|
||||
return false;
|
||||
|
||||
case 'header':
|
||||
return false;
|
||||
|
@ -1703,4 +1755,4 @@ Zotero.ItemGroup.prototype.isSearchMode = function() {
|
|||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
557
chrome/content/zotero/xpcom/commons.js
Normal file
557
chrome/content/zotero/xpcom/commons.js
Normal file
|
@ -0,0 +1,557 @@
|
|||
/*
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright (c) 2006 Center for History and New Media
|
||||
George Mason University, Fairfax, Virginia, USA
|
||||
http://chnm.gmu.edu
|
||||
|
||||
Licensed under the Educational Community License, Version 1.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.opensource.org/licenses/ecl1.php
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
||||
***** END LICENSE BLOCK *****
|
||||
*/
|
||||
|
||||
Zotero.Commons = new function() {
|
||||
|
||||
this.uri = 'http://www.archive.org/';
|
||||
|
||||
this.__defineGetter__('buckets', function () {
|
||||
if(!Zotero.Prefs.get("commons.enabled")) {
|
||||
return [];
|
||||
}
|
||||
|
||||
var accessKey = Zotero.Prefs.get("commons.accessKey");
|
||||
var secretKey = Zotero.Prefs.get("commons.secretKey");
|
||||
var buckets = [];
|
||||
var bucketNames = Zotero.Prefs.get("commons.buckets").split(',');
|
||||
for(var i = 0, len = bucketNames.length; i < len; i++) {
|
||||
if(bucketNames[i]) {
|
||||
buckets.push(new Zotero.Commons.Bucket(bucketNames[i], accessKey, secretKey));
|
||||
}
|
||||
}
|
||||
return buckets;
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// accessKey and secretKey are passed to allow easy implementation
|
||||
// of using multiple accounts
|
||||
Zotero.Commons.Bucket = function(name, accessKey, secretKey) {
|
||||
this.name = name;
|
||||
this._accessKey = accessKey;
|
||||
this._secretKey = secretKey;
|
||||
this._items = null;
|
||||
this._requestingItems = false;
|
||||
}
|
||||
|
||||
|
||||
Zotero.Commons.Bucket.prototype.apiUrl = 'http://s3.us.archive.org';
|
||||
|
||||
Zotero.Commons.Bucket.prototype.RDF_TRANSLATOR = {
|
||||
'label': 'Zotero RDF',
|
||||
'target': 'rdf',
|
||||
'translatorID': '14763d24-8ba0-45df-8f52-b8d1108e7ac9',
|
||||
'displayOptions': {
|
||||
'exportFileData': true,
|
||||
'exportNotes': true
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
Zotero.Commons.Bucket.prototype.__defineGetter__('uri', function () {
|
||||
return 'http://www.archive.org/details/' + this.name;
|
||||
});
|
||||
|
||||
Zotero.Commons.Bucket.prototype.getKeyUrl = function(name, key) {
|
||||
return 'http://' + name + '.s3.us.archive.org/' + key;
|
||||
|
||||
}
|
||||
|
||||
Zotero.Commons.Bucket.prototype.relationPredicate = "owl:sameAs";
|
||||
|
||||
// return an array of items currently stored in this bucket
|
||||
Zotero.Commons.Bucket.prototype.getItems = function() {
|
||||
/*if(this._items) {
|
||||
return this._items;
|
||||
}
|
||||
|
||||
// avoid multiple requests to IA
|
||||
if(this._requestingItems) {
|
||||
return [];
|
||||
}
|
||||
|
||||
this._requestingItems = true;
|
||||
|
||||
// get a list of keys associated with this bucket
|
||||
var method = "GET";
|
||||
var resource = '/' + this.name + '/';
|
||||
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
req.open(method, this.apiUrl + resource, true);
|
||||
//req.channel.loadFlags |= Components.interfaces.nsIRequest.LOAD_BYPASS_CACHE;
|
||||
|
||||
var self = this;
|
||||
req.onreadystatechange = function() {
|
||||
if (req.readyState == 4) {
|
||||
if(req.status < 400) {
|
||||
// TODO this is completely wrong and needs to change
|
||||
// Need to figure out IA/Zotero interaction first
|
||||
self._items = [];
|
||||
var contents = req.responseXML.getElementsByTagName("Contents");
|
||||
for(var i = 0, len = contents.length; i < len; i++) {
|
||||
var keyParts = contents[i].getElementsByTagName('Key')[0].textContent.split('.');
|
||||
if(keyParts.length == 2 && keyParts[1] == 'zip') {
|
||||
var key = keyParts[0];
|
||||
var item = Zotero.Items.getByLibraryAndKey(null, key);
|
||||
if(item) {
|
||||
self._items.push(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
Zotero.Notifier.trigger('refresh', 'bucket', 'all');
|
||||
}
|
||||
else {
|
||||
alert("Request to get the keys of bucket " + self.name + " failed.");
|
||||
}
|
||||
self._requestingItems = false;
|
||||
}
|
||||
};
|
||||
|
||||
req.send(null);
|
||||
*/
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
// upload zipped Zotero RDF output of items to this bucket
|
||||
Zotero.Commons.Bucket.prototype.uploadItems = function(ids) {
|
||||
var items = Zotero.Items.get(ids);
|
||||
if (!items) {
|
||||
return;
|
||||
}
|
||||
|
||||
var tmpDir = Zotero.getTempDirectory();
|
||||
|
||||
// export individual items through the Zotero RDF translation
|
||||
for(var i = 0, len = items.length; i < len; i++) {
|
||||
var item = items[i];
|
||||
if(item.isRegularItem()) {
|
||||
// generate file location for the export output
|
||||
var rdfExportPath = Components.classes["@mozilla.org/file/local;1"]
|
||||
.createInstance(Components.interfaces.nsILocalFile);
|
||||
rdfExportPath.initWithFile(tmpDir);
|
||||
rdfExportPath.append(item.key);
|
||||
|
||||
// initialize and run the translator for this item
|
||||
var translation = new Zotero.Translate("export");
|
||||
translation.setItems([item]);
|
||||
translation.setTranslator(this.RDF_TRANSLATOR.translatorID);
|
||||
translation.setDisplayOptions(this.RDF_TRANSLATOR.displayOptions);
|
||||
translation.setHandler("done", this._translateCallback);
|
||||
translation.setLocation(rdfExportPath);
|
||||
|
||||
// add some data to translator needed by _translateCallback
|
||||
translation._bucketData = {bucket: this, items: [item]};
|
||||
|
||||
translation.translate(); // synchronous
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Zips the output of the translation and then calls _putKey
|
||||
// Called after a translation is done.
|
||||
Zotero.Commons.Bucket.prototype._translateCallback = function(translation, successful) {
|
||||
if(!successful) {
|
||||
alert("Commons.TranslatorManager: tranlation failed for " + translation);
|
||||
}
|
||||
|
||||
var data = translation._bucketData;
|
||||
|
||||
try {
|
||||
var dir = Components.classes["@mozilla.org/file/local;1"]
|
||||
.createInstance(Components.interfaces.nsILocalFile);
|
||||
dir.initWithPath(translation.path);
|
||||
|
||||
// create zip file
|
||||
var zipFile = Zotero.getTempDirectory();
|
||||
zipFile.append(dir.leafName + '.zip');
|
||||
|
||||
var zw = Components.classes["@mozilla.org/zipwriter;1"]
|
||||
.createInstance(Components.interfaces.nsIZipWriter);
|
||||
zw.open(zipFile, 0x04 | 0x08 | 0x20); // open rw, create, truncate
|
||||
|
||||
data.bucket._zipDirectory(data.bucket, dir, dir, zw);
|
||||
|
||||
data.uploadFile = zipFile;
|
||||
// add observer so _putKey is called on zip completion
|
||||
var observer = new Zotero.Commons.ZipWriterObserver(zw, data.bucket._putKey, data);
|
||||
zw.processQueue(observer, null);
|
||||
}
|
||||
catch (e) {
|
||||
alert("Commons: Upload failed: " + e);
|
||||
}
|
||||
}
|
||||
|
||||
// Does the put call to IA, puting data.uploadFile into the bucket
|
||||
Zotero.Commons.Bucket.prototype._putKey = function(data) {
|
||||
var self = data.bucket;
|
||||
var method = "PUT";
|
||||
var mimeType = 'application/zip';
|
||||
var key = data.uploadFile.leafName;
|
||||
var resource = '/' + self.name + '/' + key;
|
||||
var content = self._readFileContents(data.uploadFile);
|
||||
|
||||
var req = Components.classes["@mozilla.org/xmlextras/xmlhttprequest;1"]
|
||||
.createInstance(Components.interfaces.nsIXMLHttpRequest);
|
||||
req.open(method, self.apiUrl + resource, true);
|
||||
|
||||
var d = new Date();
|
||||
var headers = {};
|
||||
headers["Content-Type"] = mimeType;
|
||||
headers["Content-Length"] = content.length;
|
||||
headers["Date"] = d.toUTCString();
|
||||
headers["x-amz-meta-creator"] = "Zotero Commons";
|
||||
|
||||
for(var header in headers) {
|
||||
req.setRequestHeader(header, headers[header]);
|
||||
}
|
||||
|
||||
var signature = self._generateSignature(method, resource, headers, self._secretKey);
|
||||
req.setRequestHeader("Authorization", "AWS " + self._accessKey + ":" + signature);
|
||||
//req.setRequestHeader("Authorization", "LOW " + self._accessKey + ":" + self._secretKey);
|
||||
|
||||
req.onreadystatechange = function() {
|
||||
if (req.readyState == 4) {
|
||||
if(req.status < 400) {
|
||||
for(var i = 0, len = data.items.length; i < len; i++) {
|
||||
var url1 = Zotero.URI.getItemURI(data.items[i]);
|
||||
var predicate = self.relationPredicate;
|
||||
var url2 = self.getKeyUrl(self.name, key);
|
||||
|
||||
if (Zotero.Relations.getByURIs(url1, predicate, url2).length
|
||||
|| Zotero.Relations.getByURIs(url2, predicate, url1).length) {
|
||||
Zotero.debug(url1 + " and " + url2 + " are already linked");
|
||||
continue;
|
||||
}
|
||||
Zotero.Relations.add(null, url1, predicate, url2);
|
||||
}
|
||||
Zotero.debug("Commons: " + key + " was uploaded successfully.");
|
||||
}
|
||||
else if(req.status == 403) {
|
||||
alert("Failed to upload " + key + " to IA: authentication failed.");
|
||||
}
|
||||
else if(req.status == 503) {
|
||||
alert("Failed to upload " + key + " to IA: server unavailable.");
|
||||
}
|
||||
else {
|
||||
alert("Failed to upload " + key + " to IA.");
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
req.sendAsBinary(content);
|
||||
}
|
||||
|
||||
// return the content of an input nsiFile
|
||||
Zotero.Commons.Bucket.prototype._readFileContents = function(bfile) {
|
||||
var istream = Components.classes["@mozilla.org/network/file-input-stream;1"]
|
||||
.createInstance(Components.interfaces.nsIFileInputStream);
|
||||
istream.init(bfile, -1, -1, false);
|
||||
var bstream = Components.classes["@mozilla.org/binaryinputstream;1"]
|
||||
.createInstance(Components.interfaces.nsIBinaryInputStream);
|
||||
bstream.setInputStream(istream);
|
||||
return bstream.readBytes(bstream.available());
|
||||
}
|
||||
|
||||
// Recursively add files and directories to zipWriter
|
||||
Zotero.Commons.Bucket.prototype._zipDirectory = function(self, rootDir, dir, zipWriter) {
|
||||
dir = dir.directoryEntries;
|
||||
while(dir.hasMoreElements()) {
|
||||
var file = dir.getNext();
|
||||
file.QueryInterface(Components.interfaces.nsILocalFile);
|
||||
|
||||
var fileName = file.getRelativeDescriptor(rootDir);
|
||||
if(fileName.indexOf('.') == 0) {
|
||||
Zotero.debug('Skipping file ' + fileName);
|
||||
continue;
|
||||
}
|
||||
|
||||
// addEntryFile works for both files and directories
|
||||
zipWriter.addEntryFile(
|
||||
fileName,
|
||||
Components.interfaces.nsIZipWriter.COMPRESSION_DEFAULT,
|
||||
file,
|
||||
true
|
||||
);
|
||||
|
||||
if(file.isDirectory()) {
|
||||
self._zipDirectory(self, rootDir, file, zipWriter);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Zotero.Commons.Bucket.prototype._generateSignature = function(method, resource, headers, secretKey) {
|
||||
var data = method + '\n' +
|
||||
((headers['Content-MD5']) ? headers['Content-MD5'] : '') + '\n' +
|
||||
((headers['Content-Type']) ? headers['Content-Type'] : '') + '\n' +
|
||||
((headers['Date']) ? headers['Date'] : '') + '\n';
|
||||
|
||||
// add x-amz- headers in alphabetic order
|
||||
var amz = [];
|
||||
for(header in headers) {
|
||||
if(header.indexOf("x-amz-") == 0) {
|
||||
amz.push(header + ":" + headers[header] + '\n');
|
||||
}
|
||||
}
|
||||
data += amz.sort().join('');
|
||||
|
||||
data += resource;
|
||||
|
||||
return Zotero.Commons.SHA1.b64_hmac_sha1(secretKey, data) + '=';
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Implements nsIRequestObserver
|
||||
Zotero.Commons.ZipWriterObserver = function (zipWriter, callback, callbackData) {
|
||||
this._zipWriter = zipWriter;
|
||||
this._callback = callback;
|
||||
this._callbackData = callbackData;
|
||||
}
|
||||
|
||||
Zotero.Commons.ZipWriterObserver.prototype = {
|
||||
onStartRequest: function () {},
|
||||
|
||||
onStopRequest: function(req, context, status) {
|
||||
this._zipWriter.close();
|
||||
this._callback(this._callbackData);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* A JavaScript implementation of the Secure Hash Algorithm, SHA-1, as defined
|
||||
* in FIPS PUB 180-1
|
||||
* Version 2.1a Copyright Paul Johnston 2000 - 2002.
|
||||
* Other contributors: Greg Holt, Andrew Kepert, Ydnar, Lostinet
|
||||
* Distributed under the BSD License
|
||||
* See http://pajhome.org.uk/crypt/md5 for details.
|
||||
*/
|
||||
Zotero.Commons.SHA1 = new function() {
|
||||
|
||||
// added by Ben Parr to expose function for Zotero
|
||||
this.hex_sha1 = hex_sha1;
|
||||
this.b64_sha1 = b64_sha1;
|
||||
this.str_sha1 = str_sha1;
|
||||
this.hex_hmac_sha1 = hex_hmac_sha1;
|
||||
this.b64_hmac_sha1 = b64_hmac_sha1;
|
||||
this.str_hmac_sha1 = str_hmac_sha1;
|
||||
|
||||
|
||||
/*
|
||||
* Configurable variables. You may need to tweak these to be compatible with
|
||||
* the server-side, but the defaults work in most cases.
|
||||
*/
|
||||
var hexcase = 0; /* hex output format. 0 - lowercase; 1 - uppercase */
|
||||
var b64pad = ""; /* base-64 pad character. "=" for strict RFC compliance */
|
||||
var chrsz = 8; /* bits per input character. 8 - ASCII; 16 - Unicode */
|
||||
|
||||
/*
|
||||
* These are the functions you'll usually want to call
|
||||
* They take string arguments and return either hex or base-64 encoded strings
|
||||
*/
|
||||
function hex_sha1(s){return binb2hex(core_sha1(str2binb(s),s.length * chrsz));}
|
||||
function b64_sha1(s){return binb2b64(core_sha1(str2binb(s),s.length * chrsz));}
|
||||
function str_sha1(s){return binb2str(core_sha1(str2binb(s),s.length * chrsz));}
|
||||
function hex_hmac_sha1(key, data){ return binb2hex(core_hmac_sha1(key, data));}
|
||||
function b64_hmac_sha1(key, data){ return binb2b64(core_hmac_sha1(key, data));}
|
||||
function str_hmac_sha1(key, data){ return binb2str(core_hmac_sha1(key, data));}
|
||||
|
||||
/*
|
||||
* Perform a simple self-test to see if the VM is working
|
||||
*/
|
||||
function sha1_vm_test()
|
||||
{
|
||||
return hex_sha1("abc") == "a9993e364706816aba3e25717850c26c9cd0d89d";
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the SHA-1 of an array of big-endian words, and a bit length
|
||||
*/
|
||||
function core_sha1(x, len)
|
||||
{
|
||||
/* append padding */
|
||||
x[len >> 5] |= 0x80 << (24 - len % 32);
|
||||
x[((len + 64 >> 9) << 4) + 15] = len;
|
||||
|
||||
var w = Array(80);
|
||||
var a = 1732584193;
|
||||
var b = -271733879;
|
||||
var c = -1732584194;
|
||||
var d = 271733878;
|
||||
var e = -1009589776;
|
||||
|
||||
for(var i = 0; i < x.length; i += 16)
|
||||
{
|
||||
var olda = a;
|
||||
var oldb = b;
|
||||
var oldc = c;
|
||||
var oldd = d;
|
||||
var olde = e;
|
||||
|
||||
for(var j = 0; j < 80; j++)
|
||||
{
|
||||
if(j < 16) w[j] = x[i + j];
|
||||
else w[j] = rol(w[j-3] ^ w[j-8] ^ w[j-14] ^ w[j-16], 1);
|
||||
var t = safe_add(safe_add(rol(a, 5), sha1_ft(j, b, c, d)),
|
||||
safe_add(safe_add(e, w[j]), sha1_kt(j)));
|
||||
e = d;
|
||||
d = c;
|
||||
c = rol(b, 30);
|
||||
b = a;
|
||||
a = t;
|
||||
}
|
||||
|
||||
a = safe_add(a, olda);
|
||||
b = safe_add(b, oldb);
|
||||
c = safe_add(c, oldc);
|
||||
d = safe_add(d, oldd);
|
||||
e = safe_add(e, olde);
|
||||
}
|
||||
return Array(a, b, c, d, e);
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
* Perform the appropriate triplet combination function for the current
|
||||
* iteration
|
||||
*/
|
||||
function sha1_ft(t, b, c, d)
|
||||
{
|
||||
if(t < 20) return (b & c) | ((~b) & d);
|
||||
if(t < 40) return b ^ c ^ d;
|
||||
if(t < 60) return (b & c) | (b & d) | (c & d);
|
||||
return b ^ c ^ d;
|
||||
}
|
||||
|
||||
/*
|
||||
* Determine the appropriate additive constant for the current iteration
|
||||
*/
|
||||
function sha1_kt(t)
|
||||
{
|
||||
return (t < 20) ? 1518500249 : (t < 40) ? 1859775393 :
|
||||
(t < 60) ? -1894007588 : -899497514;
|
||||
}
|
||||
|
||||
/*
|
||||
* Calculate the HMAC-SHA1 of a key and some data
|
||||
*/
|
||||
function core_hmac_sha1(key, data)
|
||||
{
|
||||
var bkey = str2binb(key);
|
||||
if(bkey.length > 16) bkey = core_sha1(bkey, key.length * chrsz);
|
||||
|
||||
var ipad = Array(16), opad = Array(16);
|
||||
for(var i = 0; i < 16; i++)
|
||||
{
|
||||
ipad[i] = bkey[i] ^ 0x36363636;
|
||||
opad[i] = bkey[i] ^ 0x5C5C5C5C;
|
||||
}
|
||||
|
||||
var hash = core_sha1(ipad.concat(str2binb(data)), 512 + data.length * chrsz);
|
||||
return core_sha1(opad.concat(hash), 512 + 160);
|
||||
}
|
||||
|
||||
/*
|
||||
* Add integers, wrapping at 2^32. This uses 16-bit operations internally
|
||||
* to work around bugs in some JS interpreters.
|
||||
*/
|
||||
function safe_add(x, y)
|
||||
{
|
||||
var lsw = (x & 0xFFFF) + (y & 0xFFFF);
|
||||
var msw = (x >> 16) + (y >> 16) + (lsw >> 16);
|
||||
return (msw << 16) | (lsw & 0xFFFF);
|
||||
}
|
||||
|
||||
/*
|
||||
* Bitwise rotate a 32-bit number to the left.
|
||||
*/
|
||||
function rol(num, cnt)
|
||||
{
|
||||
return (num << cnt) | (num >>> (32 - cnt));
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an 8-bit or 16-bit string to an array of big-endian words
|
||||
* In 8-bit function, characters >255 have their hi-byte silently ignored.
|
||||
*/
|
||||
function str2binb(str)
|
||||
{
|
||||
var bin = Array();
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for(var i = 0; i < str.length * chrsz; i += chrsz)
|
||||
bin[i>>5] |= (str.charCodeAt(i / chrsz) & mask) << (32 - chrsz - i%32);
|
||||
return bin;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of big-endian words to a string
|
||||
*/
|
||||
function binb2str(bin)
|
||||
{
|
||||
var str = "";
|
||||
var mask = (1 << chrsz) - 1;
|
||||
for(var i = 0; i < bin.length * 32; i += chrsz)
|
||||
str += String.fromCharCode((bin[i>>5] >>> (32 - chrsz - i%32)) & mask);
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of big-endian words to a hex string.
|
||||
*/
|
||||
function binb2hex(binarray)
|
||||
{
|
||||
var hex_tab = hexcase ? "0123456789ABCDEF" : "0123456789abcdef";
|
||||
var str = "";
|
||||
for(var i = 0; i < binarray.length * 4; i++)
|
||||
{
|
||||
str += hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8+4)) & 0xF) +
|
||||
hex_tab.charAt((binarray[i>>2] >> ((3 - i%4)*8)) & 0xF);
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
/*
|
||||
* Convert an array of big-endian words to a base-64 string
|
||||
*/
|
||||
function binb2b64(binarray)
|
||||
{
|
||||
var tab = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
var str = "";
|
||||
for(var i = 0; i < binarray.length * 4; i += 3)
|
||||
{
|
||||
var triplet = (((binarray[i >> 2] >> 8 * (3 - i %4)) & 0xFF) << 16)
|
||||
| (((binarray[i+1 >> 2] >> 8 * (3 - (i+1)%4)) & 0xFF) << 8 )
|
||||
| ((binarray[i+2 >> 2] >> 8 * (3 - (i+2)%4)) & 0xFF);
|
||||
for(var j = 0; j < 4; j++)
|
||||
{
|
||||
if(i * 8 + j * 6 > binarray.length * 32) str += b64pad;
|
||||
else str += tab.charAt((triplet >> 6*(3-j)) & 0x3F);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
}
|
||||
|
|
@ -49,7 +49,7 @@ Zotero.ItemTreeView = function(itemGroup, sourcesOnly)
|
|||
this._dataItems = [];
|
||||
this.rowCount = 0;
|
||||
|
||||
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'collection-item', 'share-items']);
|
||||
this._unregisterID = Zotero.Notifier.registerObserver(this, ['item', 'collection-item', 'share-items', 'bucket']);
|
||||
}
|
||||
|
||||
|
||||
|
@ -281,6 +281,11 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
|
|||
this.refresh();
|
||||
}
|
||||
}
|
||||
else if (type == 'bucket') {
|
||||
if (itemGroup.isBucket()) {
|
||||
this.refresh();
|
||||
}
|
||||
}
|
||||
else if (savedSelection.length == 1 && savedSelection[0] == ids[0]) {
|
||||
this.selection.clearSelection();
|
||||
this.rememberSelection(savedSelection);
|
||||
|
@ -2389,4 +2394,4 @@ Zotero.ItemTreeView.TreeRow.prototype.numAttachments = function(includeTrashed)
|
|||
return this.ref.numAttachments(includeTrashed);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -25,7 +25,7 @@ Zotero.Notifier = new function(){
|
|||
var _disabled = false;
|
||||
var _types = [
|
||||
'collection', 'creator', 'search', 'share', 'share-items', 'item',
|
||||
'collection-item', 'item-tag', 'tag', 'group'
|
||||
'collection-item', 'item-tag', 'tag', 'group', 'bucket'
|
||||
];
|
||||
var _inTransaction;
|
||||
var _locked = false;
|
||||
|
|
BIN
chrome/skin/default/zotero/treesource-bucket.png
Normal file
BIN
chrome/skin/default/zotero/treesource-bucket.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 390 B |
BIN
chrome/skin/default/zotero/treesource-commons.png
Normal file
BIN
chrome/skin/default/zotero/treesource-commons.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 923 B |
|
@ -19,6 +19,7 @@ var xpcomFiles = [
|
|||
'annotate',
|
||||
'attachments',
|
||||
'collectionTreeView',
|
||||
'commons',
|
||||
'csl',
|
||||
'dataServer',
|
||||
'data_access',
|
||||
|
|
|
@ -100,6 +100,12 @@ pref("extensions.zotero.integration.realWindow", false);
|
|||
// Zeroconf
|
||||
pref("extensions.zotero.zeroconf.server.enabled", false);
|
||||
|
||||
// Zotero Commons
|
||||
pref("extensions.zotero.commons.enabled", false);
|
||||
pref("extensions.zotero.commons.buckets", '');
|
||||
pref("extensions.zotero.commons.accessKey", '');
|
||||
pref("extensions.zotero.commons.secretKey", '');
|
||||
|
||||
// Annotation settings
|
||||
pref("extensions.zotero.annotations.warnOnClose", true);
|
||||
|
||||
|
@ -123,4 +129,4 @@ pref("extensions.zotero.proxies.transparent", true);
|
|||
pref("extensions.zotero.purge.creators", false);
|
||||
pref("extensions.zotero.purge.fulltext", false);
|
||||
pref("extensions.zotero.purge.items", false);
|
||||
pref("extensions.zotero.purge.tags", false);
|
||||
pref("extensions.zotero.purge.tags", false);
|
||||
|
|
Loading…
Reference in a new issue