File sync overhaul
- New promise-based architecture - Library-specific file sync queues, allowing other libraries to continue if there's an error in one library - Library-specific sync errors, with error icons next to each library - Changed file uploading in on-demand download mode, which had been missing - On-demand download progress indicator in middle pane - More accurate progress indicator - Various tweaks and bug fixes - Various future tweaks and bug fixes
This commit is contained in:
parent
4c8431ca7d
commit
bb93f019dc
|
@ -59,7 +59,7 @@
|
||||||
padding-top: 1px;
|
padding-top: 1px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zotero-tb-sync-warning[error=true]
|
#zotero-tb-sync-error[error=true]
|
||||||
{
|
{
|
||||||
margin-bottom: 2px;
|
margin-bottom: 2px;
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
visibility: hidden;
|
visibility: hidden;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zotero-tb-sync-warning {
|
#zotero-tb-sync-error {
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
182
chrome/content/zotero/bindings/filesyncstatus.xml
Normal file
182
chrome/content/zotero/bindings/filesyncstatus.xml
Normal file
|
@ -0,0 +1,182 @@
|
||||||
|
<?xml version="1.0"?>
|
||||||
|
<!--
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2012 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://zotero.org
|
||||||
|
|
||||||
|
This file is part of Zotero.
|
||||||
|
|
||||||
|
Zotero is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Zotero is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
-->
|
||||||
|
<?xml-stylesheet href="chrome://zotero/skin/overlay.css" type="text/css"?>
|
||||||
|
<!DOCTYPE bindings SYSTEM "chrome://zotero/locale/zotero.dtd">
|
||||||
|
|
||||||
|
<bindings xmlns="http://www.mozilla.org/xbl">
|
||||||
|
<binding id="file-sync-status">
|
||||||
|
<implementation>
|
||||||
|
<property name="data"
|
||||||
|
onget="return this._data;"
|
||||||
|
onset="this._data = val; this.refresh();">
|
||||||
|
</property>
|
||||||
|
|
||||||
|
<field name="_libraries">[]</field>
|
||||||
|
|
||||||
|
<method name="refresh">
|
||||||
|
<body>
|
||||||
|
<![CDATA[
|
||||||
|
var rows = this._id('rows');
|
||||||
|
|
||||||
|
// Get libraries with active downloads or uploads
|
||||||
|
var newLibraries = [];
|
||||||
|
for (var libraryID in this._data) {
|
||||||
|
if ((this._data[libraryID].download
|
||||||
|
&& !this._data[libraryID].download.finished)
|
||||||
|
||
|
||||||
|
(this._data[libraryID].upload
|
||||||
|
&& !this._data[libraryID].upload.finished)) {
|
||||||
|
newLibraries.push(parseInt(libraryID));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// If set of libraries is different, clear and recreate
|
||||||
|
var toRemove = Zotero.Utilities.arrayDiff(this._libraries, newLibraries);
|
||||||
|
var toAdd = Zotero.Utilities.arrayDiff(newLibraries, this._libraries);
|
||||||
|
if (toRemove.length || toAdd.length) {
|
||||||
|
while (rows.hasChildNodes()) {
|
||||||
|
rows.removeChild(rows.firstChild);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this._libraries = newLibraries;
|
||||||
|
|
||||||
|
// Update
|
||||||
|
if (rows.hasChildNodes()) {
|
||||||
|
for (var libraryID in this._data) {
|
||||||
|
var libraryStatus = this._data[libraryID];
|
||||||
|
|
||||||
|
// Library is finished
|
||||||
|
if (newLibraries.indexOf(parseInt(libraryID)) == -1) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var libraryNameRow = this._id('library-name-row-' + libraryID);
|
||||||
|
var downloadsRow = this._id('downloads-row-' + libraryID);
|
||||||
|
var uploadsRow = this._id('uploads-row-' + libraryID);
|
||||||
|
|
||||||
|
downloadsRow.lastChild.setAttribute('value',
|
||||||
|
libraryStatus.download
|
||||||
|
? libraryStatus.download.statusString
|
||||||
|
: Zotero.getString('sync.storage.none'));
|
||||||
|
uploadsRow.lastChild.setAttribute('value',
|
||||||
|
libraryStatus.upload
|
||||||
|
? libraryStatus.upload.statusString
|
||||||
|
: Zotero.getString('sync.storage.none'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Build from scratch
|
||||||
|
else {
|
||||||
|
// Get ordered list of library names
|
||||||
|
var libraryNames = [];
|
||||||
|
for each(var libraryID in newLibraries) {
|
||||||
|
libraryNames.push({
|
||||||
|
libraryID: libraryID,
|
||||||
|
name: Zotero.Libraries.getName(libraryID)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
var collation = Zotero.getLocaleCollation();
|
||||||
|
libraryNames.sort(function (a, b) {
|
||||||
|
if (a.libraryID == 0) {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (b.libraryID == 0) {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
return collation.compareString(1, a.name, b.name);
|
||||||
|
});
|
||||||
|
|
||||||
|
for (var i in libraryNames) {
|
||||||
|
var libraryID = libraryNames[i].libraryID;
|
||||||
|
var libraryStatus = this._data[libraryID];
|
||||||
|
|
||||||
|
var label = document.createElement('label');
|
||||||
|
label.id = 'library-name-row-' + libraryID;
|
||||||
|
label.setAttribute('value', libraryNames[i].name);
|
||||||
|
rows.appendChild(label);
|
||||||
|
|
||||||
|
var row = this._createRow('download',
|
||||||
|
libraryStatus.download
|
||||||
|
? libraryStatus.download.statusString
|
||||||
|
: false);
|
||||||
|
row.id = 'downloads-row-' + libraryID;
|
||||||
|
rows.appendChild(row);
|
||||||
|
|
||||||
|
var row = this._createRow('upload',
|
||||||
|
libraryStatus.upload
|
||||||
|
? libraryStatus.upload.statusString
|
||||||
|
: false);
|
||||||
|
row.id = 'uploads-row-' + libraryID;
|
||||||
|
rows.appendChild(row);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]]>
|
||||||
|
</body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_createRow">
|
||||||
|
<parameter name="type"/>
|
||||||
|
<parameter name="value"/>
|
||||||
|
<body>
|
||||||
|
<![CDATA[
|
||||||
|
var row = document.createElement('row');
|
||||||
|
|
||||||
|
var label = document.createElement('label');
|
||||||
|
label.setAttribute('value', Zotero.getString('sync.storage.' + type + 's'));
|
||||||
|
row.appendChild(label);
|
||||||
|
|
||||||
|
label = document.createElement('label');
|
||||||
|
label.setAttribute('value',
|
||||||
|
value ? value : Zotero.getString('sync.storage.none'));
|
||||||
|
row.appendChild(label);
|
||||||
|
|
||||||
|
return row;
|
||||||
|
]]>
|
||||||
|
</body>
|
||||||
|
</method>
|
||||||
|
|
||||||
|
<method name="_id">
|
||||||
|
<parameter name="id"/>
|
||||||
|
<body>
|
||||||
|
<![CDATA[
|
||||||
|
return document.getAnonymousNodes(this)[0].getElementsByAttribute('id', id)[0];
|
||||||
|
]]>
|
||||||
|
</body>
|
||||||
|
</method>
|
||||||
|
</implementation>
|
||||||
|
|
||||||
|
<content>
|
||||||
|
<grid xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul" flex="1">
|
||||||
|
<columns>
|
||||||
|
<column/>
|
||||||
|
<column/>
|
||||||
|
</columns>
|
||||||
|
<rows id="rows"/>
|
||||||
|
</grid>
|
||||||
|
</content>
|
||||||
|
|
||||||
|
</binding>
|
||||||
|
</bindings>
|
|
@ -240,7 +240,7 @@ Zotero.CollectionTreeView.prototype.reload = function()
|
||||||
*/
|
*/
|
||||||
Zotero.CollectionTreeView.prototype.notify = function(action, type, ids)
|
Zotero.CollectionTreeView.prototype.notify = function(action, type, ids)
|
||||||
{
|
{
|
||||||
if ((!ids || ids.length == 0) && action != 'refresh') {
|
if ((!ids || ids.length == 0) && action != 'refresh' && action != 'redraw') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -254,6 +254,11 @@ Zotero.CollectionTreeView.prototype.notify = function(action, type, ids)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (action == 'redraw') {
|
||||||
|
this._treebox.invalidate();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.selection.selectEventsSuppressed = true;
|
this.selection.selectEventsSuppressed = true;
|
||||||
var savedSelection = this.saveSelection();
|
var savedSelection = this.saveSelection();
|
||||||
|
|
||||||
|
@ -420,8 +425,12 @@ Zotero.CollectionTreeView.prototype.getCellText = function(row, column)
|
||||||
{
|
{
|
||||||
var obj = this._getItemAtRow(row);
|
var obj = this._getItemAtRow(row);
|
||||||
|
|
||||||
if(column.id == "zotero-collections-name-column")
|
if (column.id == 'zotero-collections-name-column') {
|
||||||
return obj.getName();
|
return obj.getName();
|
||||||
|
}
|
||||||
|
else if (column.id == 'zotero-collections-sync-status-column') {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
else
|
else
|
||||||
return "";
|
return "";
|
||||||
}
|
}
|
||||||
|
@ -430,7 +439,41 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col)
|
||||||
{
|
{
|
||||||
var itemGroup = this._getItemAtRow(row);
|
var itemGroup = this._getItemAtRow(row);
|
||||||
var collectionType = itemGroup.type;
|
var collectionType = itemGroup.type;
|
||||||
|
|
||||||
|
if (collectionType == 'group') {
|
||||||
|
collectionType = 'library';
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show sync icons only in library rows
|
||||||
|
if (collectionType != 'library' && col.index != 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
switch (collectionType) {
|
switch (collectionType) {
|
||||||
|
case 'library':
|
||||||
|
if (col.id == 'zotero-collections-sync-status-column') {
|
||||||
|
if (itemGroup.isLibrary(true)) {
|
||||||
|
var libraryID = itemGroup.isLibrary() ? 0 : itemGroup.ref.libraryID;
|
||||||
|
var errors = Zotero.Sync.Runner.getErrors(libraryID);
|
||||||
|
if (errors) {
|
||||||
|
var e = Zotero.Sync.Runner.getPrimaryError(errors);
|
||||||
|
switch (e.status) {
|
||||||
|
case 'warning':
|
||||||
|
var image = 'error';
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
var image = 'exclamation';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'chrome://zotero/skin/' + image + '.png';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case 'trash':
|
case 'trash':
|
||||||
if (this._trashNotEmpty[itemGroup.ref.libraryID ? itemGroup.ref.libraryID : 0]) {
|
if (this._trashNotEmpty[itemGroup.ref.libraryID ? itemGroup.ref.libraryID : 0]) {
|
||||||
collectionType += '-full';
|
collectionType += '-full';
|
||||||
|
@ -446,7 +489,7 @@ Zotero.CollectionTreeView.prototype.getImageSrc = function(row, col)
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 'group':
|
|
||||||
collectionType = 'library';
|
collectionType = 'library';
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -112,7 +112,7 @@ Zotero.DataObjects = function (object, objectPlural, id, table) {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
sql += "libraryID";
|
sql += "libraryID";
|
||||||
if (libraryID) {
|
if (libraryID && libraryID !== '0') {
|
||||||
sql += "=? ";
|
sql += "=? ";
|
||||||
params.push(libraryID);
|
params.push(libraryID);
|
||||||
}
|
}
|
||||||
|
|
|
@ -674,7 +674,7 @@ Zotero.Item.prototype.setField = function(field, value, loadIn) {
|
||||||
|
|
||||||
this._disabledCheck();
|
this._disabledCheck();
|
||||||
|
|
||||||
//Zotero.debug("Setting field '" + field + "' to '" + value + "' (loadIn: " + (loadIn ? 'true' : 'false') + ")");
|
//Zotero.debug("Setting field '" + field + "' to '" + value + "' (loadIn: " + (loadIn ? 'true' : 'false') + ") for item " + this.id + " ");
|
||||||
|
|
||||||
if (!field) {
|
if (!field) {
|
||||||
throw ("Field not specified in Item.setField()");
|
throw ("Field not specified in Item.setField()");
|
||||||
|
@ -1609,6 +1609,7 @@ Zotero.Item.prototype.save = function() {
|
||||||
'libraryID',
|
'libraryID',
|
||||||
'key'
|
'key'
|
||||||
];
|
];
|
||||||
|
|
||||||
for each(var field in updateFields) {
|
for each(var field in updateFields) {
|
||||||
if (this._changedPrimaryData && this._changedPrimaryData[field]) {
|
if (this._changedPrimaryData && this._changedPrimaryData[field]) {
|
||||||
sql += field + '=?, ';
|
sql += field + '=?, ';
|
||||||
|
@ -3000,7 +3001,6 @@ Zotero.Item.prototype.__defineSetter__('attachmentLinkMode', function (val) {
|
||||||
if (val === this.attachmentLinkMode) {
|
if (val === this.attachmentLinkMode) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this._changedAttachmentData) {
|
if (!this._changedAttachmentData) {
|
||||||
this._changedAttachmentData = {};
|
this._changedAttachmentData = {};
|
||||||
}
|
}
|
||||||
|
|
|
@ -45,6 +45,10 @@ Zotero.Libraries = new function () {
|
||||||
|
|
||||||
|
|
||||||
this.getName = function (libraryID) {
|
this.getName = function (libraryID) {
|
||||||
|
if (!libraryID) {
|
||||||
|
return Zotero.getString('pane.collections.library');
|
||||||
|
}
|
||||||
|
|
||||||
var type = this.getType(libraryID);
|
var type = this.getType(libraryID);
|
||||||
switch (type) {
|
switch (type) {
|
||||||
case 'group':
|
case 'group':
|
||||||
|
@ -59,6 +63,9 @@ Zotero.Libraries = new function () {
|
||||||
|
|
||||||
|
|
||||||
this.getType = function (libraryID) {
|
this.getType = function (libraryID) {
|
||||||
|
if (libraryID === 0) {
|
||||||
|
return 'user';
|
||||||
|
}
|
||||||
var sql = "SELECT libraryType FROM libraries WHERE libraryID=?";
|
var sql = "SELECT libraryType FROM libraries WHERE libraryID=?";
|
||||||
var libraryType = Zotero.DB.valueQuery(sql, libraryID);
|
var libraryType = Zotero.DB.valueQuery(sql, libraryID);
|
||||||
if (!libraryType) {
|
if (!libraryType) {
|
||||||
|
|
|
@ -402,7 +402,8 @@ Zotero.DBConnection.prototype.getStatement = function (sql, params, checkParams)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (checkParams && numParams > 0) {
|
if (checkParams && numParams > 0) {
|
||||||
throw ("No parameters provided for query containing placeholders");
|
throw ("No parameters provided for query containing placeholders "
|
||||||
|
+ "[QUERY: " + sql + "]");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return statement;
|
return statement;
|
||||||
|
|
|
@ -344,9 +344,29 @@ Zotero.ItemTreeView.prototype.notify = function(action, type, ids, extraData)
|
||||||
var savedSelection = this.saveSelection();
|
var savedSelection = this.saveSelection();
|
||||||
var previousRow = false;
|
var previousRow = false;
|
||||||
|
|
||||||
// Redraw the tree (for tag color changes)
|
// Redraw the tree (for tag color and progress changes)
|
||||||
if (action == 'redraw') {
|
if (action == 'redraw') {
|
||||||
this._treebox.invalidate();
|
// Redraw specific rows
|
||||||
|
if (type == 'item' && ids.length) {
|
||||||
|
// Redraw specific cells
|
||||||
|
if (extraData && extraData.column) {
|
||||||
|
var col = this._treebox.columns.getNamedColumn(
|
||||||
|
'zotero-items-column-' + extraData.column
|
||||||
|
);
|
||||||
|
for each(var id in ids) {
|
||||||
|
this._treebox.invalidateCell(this._itemRowMap[id], col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for each(var id in ids) {
|
||||||
|
this._treebox.invalidateRow(this._itemRowMap[id]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Redraw the whole tree
|
||||||
|
else {
|
||||||
|
this._treebox.invalidate();
|
||||||
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -849,6 +869,12 @@ Zotero.ItemTreeView.prototype.getImageSrc = function(row, col)
|
||||||
if (this._itemGroup.isTrash()) return false;
|
if (this._itemGroup.isTrash()) return false;
|
||||||
|
|
||||||
var treerow = this._getItemAtRow(row);
|
var treerow = this._getItemAtRow(row);
|
||||||
|
|
||||||
|
if ((!this.isContainer(row) || !this.isContainerOpen(row))
|
||||||
|
&& Zotero.Sync.Storage.getItemDownloadImageNumber(treerow.ref)) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
if (treerow.level === 0) {
|
if (treerow.level === 0) {
|
||||||
if (treerow.ref.isRegularItem()) {
|
if (treerow.ref.isRegularItem()) {
|
||||||
switch (treerow.ref.getBestAttachmentState()) {
|
switch (treerow.ref.getBestAttachmentState()) {
|
||||||
|
@ -2746,7 +2772,8 @@ Zotero.ItemTreeView.prototype.getRowProperties = function(row, prop) {
|
||||||
}
|
}
|
||||||
Zotero.ItemTreeView.prototype.getColumnProperties = function(col, prop) { }
|
Zotero.ItemTreeView.prototype.getColumnProperties = function(col, prop) { }
|
||||||
Zotero.ItemTreeView.prototype.getCellProperties = function(row, col, prop) {
|
Zotero.ItemTreeView.prototype.getCellProperties = function(row, col, prop) {
|
||||||
var itemID = this._getItemAtRow(row).ref.id;
|
var treeRow = this._getItemAtRow(row);
|
||||||
|
var itemID = treeRow.ref.id;
|
||||||
|
|
||||||
// Set tag colors
|
// Set tag colors
|
||||||
//
|
//
|
||||||
|
@ -2767,6 +2794,30 @@ Zotero.ItemTreeView.prototype.getCellProperties = function(row, col, prop) {
|
||||||
getService(Components.interfaces.nsIAtomService);
|
getService(Components.interfaces.nsIAtomService);
|
||||||
prop.AppendElement(aServ.getAtom("contextRow"));
|
prop.AppendElement(aServ.getAtom("contextRow"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Mark hasAttachment column, which needs special image handling
|
||||||
|
if (col.id == 'zotero-items-column-hasAttachment') {
|
||||||
|
var aServ = Components.classes["@mozilla.org/atom-service;1"].
|
||||||
|
getService(Components.interfaces.nsIAtomService);
|
||||||
|
prop.AppendElement(aServ.getAtom("hasAttachment"));
|
||||||
|
|
||||||
|
// Don't show pie for open parent items, since we show it for the
|
||||||
|
// child item
|
||||||
|
if (this.isContainer(row) && this.isContainerOpen(row)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var num = Zotero.Sync.Storage.getItemDownloadImageNumber(treeRow.ref);
|
||||||
|
//var num = Math.round(new Date().getTime() % 10000 / 10000 * 64);
|
||||||
|
if (num !== false) {
|
||||||
|
if (!aServ) {
|
||||||
|
var aServ = Components.classes["@mozilla.org/atom-service;1"].
|
||||||
|
getService(Components.interfaces.nsIAtomService);
|
||||||
|
}
|
||||||
|
prop.AppendElement(aServ.getAtom("pie"));
|
||||||
|
prop.AppendElement(aServ.getAtom("pie" + num));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.ItemTreeView.TreeRow = function(ref, level, isOpen)
|
Zotero.ItemTreeView.TreeRow = function(ref, level, isOpen)
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
94
chrome/content/zotero/xpcom/storage/eventLog.js
Normal file
94
chrome/content/zotero/xpcom/storage/eventLog.js
Normal file
|
@ -0,0 +1,94 @@
|
||||||
|
/*
|
||||||
|
***** BEGIN LICENSE BLOCK *****
|
||||||
|
|
||||||
|
Copyright © 2012 Center for History and New Media
|
||||||
|
George Mason University, Fairfax, Virginia, USA
|
||||||
|
http://zotero.org
|
||||||
|
|
||||||
|
This file is part of Zotero.
|
||||||
|
|
||||||
|
Zotero is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU Affero General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
Zotero is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU Affero General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU Affero General Public License
|
||||||
|
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
***** END LICENSE BLOCK *****
|
||||||
|
*/
|
||||||
|
|
||||||
|
Zotero.Sync.Storage.EventLog = (function () {
|
||||||
|
// Non-library-specific
|
||||||
|
var _general = { warnings: [], errors: [] };
|
||||||
|
// Library-specific
|
||||||
|
var _warnings = {};
|
||||||
|
var _errors = {};
|
||||||
|
|
||||||
|
function call(type, data, libraryID) {
|
||||||
|
if (libraryID) {
|
||||||
|
switch (type) {
|
||||||
|
case 'warning':
|
||||||
|
var target = _general.warnings;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
var target = _general.errors;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
switch (type) {
|
||||||
|
case 'warning':
|
||||||
|
var target = _warnings;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'error':
|
||||||
|
var target = _errors;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!target[libraryID]) {
|
||||||
|
target[libraryID] = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
target[libraryID].push(data);
|
||||||
|
|
||||||
|
Zotero.debug(data, type == 'error' ? 1 : 2);
|
||||||
|
Components.utils.reportError(new Error(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
error: function (e, libraryID) call('error', e, libraryID),
|
||||||
|
warning: function (e, libraryID) call('warning', e, libraryID),
|
||||||
|
|
||||||
|
clear: function (libraryID) {
|
||||||
|
var queues = Zotero.Sync.Storage.QueueManager.getAll();
|
||||||
|
for each(var queue in queues) {
|
||||||
|
if (queue.isRunning()) {
|
||||||
|
Zotero.debug(queue.name[0].toUpperCase() + queue.name.substr(1)
|
||||||
|
+ " queue not empty -- not clearing storage sync event observers");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof libraryID == 'undefined') {
|
||||||
|
Zotero.debug("Clearing file sync event log");
|
||||||
|
_general = { warnings: [], errors: [] };
|
||||||
|
_warnings = {};
|
||||||
|
_errors = {};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug("Clearing file sync event log for library " + libraryID);
|
||||||
|
_warnings[libraryID] = [];
|
||||||
|
_errors[libraryID] = [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}());
|
|
@ -1,143 +0,0 @@
|
||||||
/*
|
|
||||||
***** BEGIN LICENSE BLOCK *****
|
|
||||||
|
|
||||||
Copyright © 2009 Center for History and New Media
|
|
||||||
George Mason University, Fairfax, Virginia, USA
|
|
||||||
http://zotero.org
|
|
||||||
|
|
||||||
This file is part of Zotero.
|
|
||||||
|
|
||||||
Zotero is free software: you can redistribute it and/or modify
|
|
||||||
it under the terms of the GNU Affero General Public License as published by
|
|
||||||
the Free Software Foundation, either version 3 of the License, or
|
|
||||||
(at your option) any later version.
|
|
||||||
|
|
||||||
Zotero is distributed in the hope that it will be useful,
|
|
||||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
GNU Affero General Public License for more details.
|
|
||||||
|
|
||||||
You should have received a copy of the GNU Affero General Public License
|
|
||||||
along with Zotero. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
***** END LICENSE BLOCK *****
|
|
||||||
*/
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.EventManager = (function () {
|
|
||||||
var _observers = [];
|
|
||||||
|
|
||||||
function call(handler, data, clear) {
|
|
||||||
Zotero.debug("Calling storage sync " + handler + " handlers");
|
|
||||||
|
|
||||||
var observers = _observers;
|
|
||||||
var cont = true;
|
|
||||||
var handled = false;
|
|
||||||
|
|
||||||
if (clear) {
|
|
||||||
Zotero.Sync.Storage.EventManager.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Process most recently assigned observers first
|
|
||||||
for (var i = observers.length - 1; i >= 0; i--) {
|
|
||||||
let observer = observers[i].observer;
|
|
||||||
let j = i;
|
|
||||||
if (observer[handler]) {
|
|
||||||
handled = true;
|
|
||||||
if (observers[i].async) {
|
|
||||||
setTimeout(function () {
|
|
||||||
Zotero.debug("Calling " + handler + " handler " + j);
|
|
||||||
var cont = observer[handler](data);
|
|
||||||
if (cont === false) {
|
|
||||||
throw new Error("Cannot cancel events from async observer");
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Zotero.debug("Calling " + handler + " handler " + j);
|
|
||||||
var cont = observer[handler](data);
|
|
||||||
// If handler returns explicit false, cancel further events
|
|
||||||
if (cont === false) {
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!handled && data) {
|
|
||||||
var msg = "Unhandled storage sync event: " + data;
|
|
||||||
Zotero.debug(msg, 1);
|
|
||||||
if (handler == 'onError') {
|
|
||||||
throw new Error(msg);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Components.utils.reportError(msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Throw errors to stop execution
|
|
||||||
if (handler == 'onError') {
|
|
||||||
if (!data) {
|
|
||||||
throw new Error("Data not provided for error");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cont !== false) {
|
|
||||||
throw (data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
registerObserver: function (observer, async, id) {
|
|
||||||
var pos = -1;
|
|
||||||
|
|
||||||
if (id) {
|
|
||||||
for (var i = 0, len = _observers.length; i < len; i++) {
|
|
||||||
var o = _observers[i];
|
|
||||||
if (o.id === id && o.async == async) {
|
|
||||||
pos = o;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (pos == -1) {
|
|
||||||
Zotero.debug("Registering storage sync event observer '" + id + "'");
|
|
||||||
_observers.push({
|
|
||||||
observer: observer,
|
|
||||||
async: !!async,
|
|
||||||
id: id
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Zotero.debug("Replacing storage sync event observer '" + id + "'");
|
|
||||||
_observers[pos] = {
|
|
||||||
observer: observer,
|
|
||||||
async: !!async,
|
|
||||||
id: id
|
|
||||||
};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
success: function () call('onSuccess', false, true),
|
|
||||||
skip: function (clear) call('onSkip', false, true),
|
|
||||||
stop: function () call('onStop', false, true),
|
|
||||||
error: function (e) call('onError', e, true),
|
|
||||||
|
|
||||||
warning: function (e) call('onWarning', e),
|
|
||||||
changesMade: function () call('onChangesMade'),
|
|
||||||
|
|
||||||
clear: function () {
|
|
||||||
var queues = Zotero.Sync.Storage.QueueManager.getAll();
|
|
||||||
for each(var queue in queues) {
|
|
||||||
if (queue.isRunning()) {
|
|
||||||
Zotero.debug(queue.name[0].toUpperCase() + queue.name.substr(1)
|
|
||||||
+ " queue not empty -- not clearing storage sync event observers");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.debug("Clearing storage sync event observers");
|
|
||||||
_observers = [];
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}());
|
|
|
@ -26,159 +26,62 @@
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode = function () {};
|
Zotero.Sync.Storage.Mode = function () {};
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.__defineGetter__('enabled', function () {
|
|
||||||
try {
|
|
||||||
return this._enabled;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.__defineGetter__('verified', function () {
|
Zotero.Sync.Storage.Mode.prototype.__defineGetter__('verified', function () {
|
||||||
try {
|
return this._verified;
|
||||||
return this._verified;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.__defineGetter__('active', function () {
|
|
||||||
try {
|
|
||||||
return this._enabled && this._verified && this._initFromPrefs();
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.__defineGetter__('username', function () {
|
Zotero.Sync.Storage.Mode.prototype.__defineGetter__('username', function () {
|
||||||
try {
|
return this._username;
|
||||||
return this._username;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.__defineGetter__('password', function () {
|
Zotero.Sync.Storage.Mode.prototype.__defineGetter__('password', function () {
|
||||||
try {
|
return this._password;
|
||||||
return this._password;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.__defineSetter__('password', function (val) {
|
Zotero.Sync.Storage.Mode.prototype.__defineSetter__('password', function (val) {
|
||||||
try {
|
this._password = val;
|
||||||
this._password = val;
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.init = function () {
|
Zotero.Sync.Storage.Mode.prototype.init = function () {
|
||||||
try {
|
return this._init();
|
||||||
return this._init();
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.initFromPrefs = function () {
|
|
||||||
try {
|
|
||||||
return this._initFromPrefs();
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.sync = function (observer) {
|
Zotero.Sync.Storage.Mode.prototype.sync = function (observer) {
|
||||||
Zotero.Sync.Storage.sync(this.name, observer);
|
return Zotero.Sync.Storage.sync(this.name, observer);
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.downloadFile = function (request) {
|
Zotero.Sync.Storage.Mode.prototype.downloadFile = function (request) {
|
||||||
try {
|
return this._downloadFile(request);
|
||||||
this._downloadFile(request);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.uploadFile = function (request) {
|
Zotero.Sync.Storage.Mode.prototype.uploadFile = function (request) {
|
||||||
try {
|
return this._uploadFile(request);
|
||||||
this._uploadFile(request);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.getLastSyncTime = function (callback) {
|
Zotero.Sync.Storage.Mode.prototype.getLastSyncTime = function (libraryID) {
|
||||||
try {
|
return this._getLastSyncTime(libraryID);
|
||||||
this._getLastSyncTime(callback);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.setLastSyncTime = function (callback, useLastSyncTime) {
|
Zotero.Sync.Storage.Mode.prototype.setLastSyncTime = function (callback, useLastSyncTime) {
|
||||||
try {
|
return this._setLastSyncTime(callback, useLastSyncTime);
|
||||||
this._setLastSyncTime(callback, useLastSyncTime);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.checkServer = function (callback) {
|
Zotero.Sync.Storage.Mode.prototype.checkServer = function (callback) {
|
||||||
try {
|
return this._checkServer(callback);
|
||||||
return this._checkServer(callback);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.checkServerCallback = function (uri, status, window, skipSuccessMessage) {
|
Zotero.Sync.Storage.Mode.prototype.checkServerCallback = function (uri, status, window, skipSuccessMessage) {
|
||||||
try {
|
return this._checkServerCallback(uri, status, window, skipSuccessMessage);
|
||||||
return this._checkServerCallback(uri, status, window, skipSuccessMessage);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.cacheCredentials = function (callback) {
|
Zotero.Sync.Storage.Mode.prototype.cacheCredentials = function (callback) {
|
||||||
try {
|
return this._cacheCredentials(callback);
|
||||||
return this._cacheCredentials(callback);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.purgeDeletedStorageFiles = function (callback) {
|
Zotero.Sync.Storage.Mode.prototype.purgeDeletedStorageFiles = function (callback) {
|
||||||
try {
|
return this._purgeDeletedStorageFiles(callback);
|
||||||
this._purgeDeletedStorageFiles(callback);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Mode.prototype.purgeOrphanedStorageFiles = function (callback) {
|
Zotero.Sync.Storage.Mode.prototype.purgeOrphanedStorageFiles = function (callback) {
|
||||||
try {
|
return this._purgeOrphanedStorageFiles(callback);
|
||||||
this._purgeOrphanedStorageFiles(callback);
|
|
||||||
}
|
|
||||||
catch (e) {
|
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,13 +26,14 @@
|
||||||
/**
|
/**
|
||||||
* Queue for storage sync transfer requests
|
* Queue for storage sync transfer requests
|
||||||
*
|
*
|
||||||
* @param {String} name Queue name (e.g., 'download' or 'upload')
|
* @param {String} type Queue type (e.g., 'download' or 'upload')
|
||||||
*/
|
*/
|
||||||
Zotero.Sync.Storage.Queue = function (name) {
|
Zotero.Sync.Storage.Queue = function (type, libraryID) {
|
||||||
Zotero.debug("Initializing " + name + " queue");
|
Zotero.debug("Initializing " + type + " queue for library " + libraryID);
|
||||||
|
|
||||||
// Public properties
|
// Public properties
|
||||||
this.name = name;
|
this.type = type;
|
||||||
|
this.libraryID = libraryID;
|
||||||
this.maxConcurrentRequests = 1;
|
this.maxConcurrentRequests = 1;
|
||||||
this.activeRequests = 0;
|
this.activeRequests = 0;
|
||||||
this.totalRequests = 0;
|
this.totalRequests = 0;
|
||||||
|
@ -42,16 +43,25 @@ Zotero.Sync.Storage.Queue = function (name) {
|
||||||
this._highPriority = [];
|
this._highPriority = [];
|
||||||
this._running = false;
|
this._running = false;
|
||||||
this._stopping = false;
|
this._stopping = false;
|
||||||
|
this._finished = false;
|
||||||
|
this._error = false;
|
||||||
this._finishedReqs = 0;
|
this._finishedReqs = 0;
|
||||||
this._lastTotalRequests = 0;
|
this._localChanges = false;
|
||||||
|
this._remoteChanges = false;
|
||||||
|
this._conflicts = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('Name', function () {
|
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('name', function () {
|
||||||
return this.name[0].toUpperCase() + this.name.substr(1);
|
return this.type + "/" + this.libraryID;
|
||||||
|
});
|
||||||
|
|
||||||
|
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('Type', function () {
|
||||||
|
return this.type[0].toUpperCase() + this.type.substr(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('running', function () this._running);
|
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('running', function () this._running);
|
||||||
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('stopping', function () this._stopping);
|
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('stopping', function () this._stopping);
|
||||||
|
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('finished', function () this._finished);
|
||||||
|
|
||||||
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('unfinishedRequests', function () {
|
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('unfinishedRequests', function () {
|
||||||
return this.totalRequests - this.finishedRequests;
|
return this.totalRequests - this.finishedRequests;
|
||||||
|
@ -73,22 +83,51 @@ Zotero.Sync.Storage.Queue.prototype.__defineSetter__('finishedRequests', functio
|
||||||
|
|
||||||
// Last request
|
// Last request
|
||||||
if (val == this.totalRequests) {
|
if (val == this.totalRequests) {
|
||||||
Zotero.debug(this.Name + " queue is done");
|
Zotero.debug(this.Type + " queue is done for library " + this.libraryID);
|
||||||
|
|
||||||
// DEBUG info
|
// DEBUG info
|
||||||
Zotero.debug("Active requests: " + this.activeRequests);
|
Zotero.debug("Active requests: " + this.activeRequests);
|
||||||
|
|
||||||
if (this.activeRequests) {
|
if (this.activeRequests) {
|
||||||
throw new Error(this.Name + " queue can't be done if there are active requests");
|
throw new Error(this.Type + " queue for library " + this.libraryID
|
||||||
|
+ " can't be done if there are active requests");
|
||||||
}
|
}
|
||||||
|
|
||||||
this._running = false;
|
this._running = false;
|
||||||
this._stopping = false;
|
this._stopping = false;
|
||||||
|
this._finished = true;
|
||||||
this._requests = {};
|
this._requests = {};
|
||||||
this._highPriority = [];
|
this._highPriority = [];
|
||||||
this._finishedReqs = 0;
|
|
||||||
this._lastTotalRequests = this.totalRequests;
|
var localChanges = this._localChanges;
|
||||||
this.totalRequests = 0;
|
var remoteChanges = this._remoteChanges;
|
||||||
|
var conflicts = this._conflicts.concat();
|
||||||
|
this._localChanges = false;
|
||||||
|
this._remoteChanges = false;
|
||||||
|
this._conflicts = [];
|
||||||
|
|
||||||
|
if (!this._error) {
|
||||||
|
Zotero.debug("Resolving promise for queue " + this.name);
|
||||||
|
Zotero.debug(this._localChanges);
|
||||||
|
Zotero.debug(this._remoteChanges);
|
||||||
|
Zotero.debug(this._conflicts);
|
||||||
|
|
||||||
|
this._deferred.resolve({
|
||||||
|
libraryID: this.libraryID,
|
||||||
|
type: this.type,
|
||||||
|
localChanges: localChanges,
|
||||||
|
remoteChanges: remoteChanges,
|
||||||
|
conflicts: conflicts
|
||||||
|
});
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug("Rejecting promise for queue " + this.name);
|
||||||
|
var e = this._error;
|
||||||
|
this._error = false;
|
||||||
|
e.libraryID = this.libraryID;
|
||||||
|
e.type = this.type;
|
||||||
|
this._deferred.reject(e);
|
||||||
|
}
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -99,10 +138,6 @@ Zotero.Sync.Storage.Queue.prototype.__defineSetter__('finishedRequests', functio
|
||||||
this.advance();
|
this.advance();
|
||||||
});
|
});
|
||||||
|
|
||||||
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('lastTotalRequests', function () {
|
|
||||||
return this._lastTotalRequests;
|
|
||||||
});
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('queuedRequests', function () {
|
Zotero.Sync.Storage.Queue.prototype.__defineGetter__('queuedRequests', function () {
|
||||||
return this.unfinishedRequests - this.activeRequests;
|
return this.unfinishedRequests - this.activeRequests;
|
||||||
});
|
});
|
||||||
|
@ -119,6 +154,9 @@ Zotero.Sync.Storage.Queue.prototype.__defineGetter__('percentage', function () {
|
||||||
if (this.totalRequests == 0) {
|
if (this.totalRequests == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
if (this._finished) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
var completedRequests = 0;
|
var completedRequests = 0;
|
||||||
for each(var request in this._requests) {
|
for each(var request in this._requests) {
|
||||||
|
@ -144,9 +182,13 @@ Zotero.Sync.Storage.Queue.prototype.isStopping = function () {
|
||||||
* @param {Boolean} highPriority Add or move request to high priority queue
|
* @param {Boolean} highPriority Add or move request to high priority queue
|
||||||
*/
|
*/
|
||||||
Zotero.Sync.Storage.Queue.prototype.addRequest = function (request, highPriority) {
|
Zotero.Sync.Storage.Queue.prototype.addRequest = function (request, highPriority) {
|
||||||
|
if (this._finished) {
|
||||||
|
this.reset();
|
||||||
|
}
|
||||||
|
|
||||||
request.queue = this;
|
request.queue = this;
|
||||||
var name = request.name;
|
var name = request.name;
|
||||||
Zotero.debug("Queuing " + this.name + " request '" + name + "'");
|
Zotero.debug("Queuing " + this.type + " request '" + name + "' for library " + this.libraryID);
|
||||||
|
|
||||||
if (this._requests[name]) {
|
if (this._requests[name]) {
|
||||||
if (highPriority) {
|
if (highPriority) {
|
||||||
|
@ -166,56 +208,133 @@ Zotero.Sync.Storage.Queue.prototype.addRequest = function (request, highPriority
|
||||||
if (highPriority) {
|
if (highPriority) {
|
||||||
this._highPriority.push(name);
|
this._highPriority.push(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.advance();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Sync.Storage.Queue.prototype.start = function () {
|
||||||
|
if (!this._deferred || this._deferred.promise.isResolved()) {
|
||||||
|
Zotero.debug("Creating deferred for queue " + this.name);
|
||||||
|
this._deferred = Q.defer();
|
||||||
|
}
|
||||||
|
// The queue manager needs to know what queues were running in the
|
||||||
|
// current session
|
||||||
|
Zotero.Sync.Storage.QueueManager.addCurrentQueue(this);
|
||||||
|
this.advance();
|
||||||
|
return this._deferred.promise;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start another request in this queue if there's an available slot
|
* Start another request in this queue if there's an available slot
|
||||||
*/
|
*/
|
||||||
Zotero.Sync.Storage.Queue.prototype.advance = function () {
|
Zotero.Sync.Storage.Queue.prototype.advance = function () {
|
||||||
this._running = true;
|
this._running = true;
|
||||||
|
this._finished = false;
|
||||||
|
|
||||||
if (this._stopping) {
|
if (this._stopping) {
|
||||||
Zotero.debug(this.Name + " queue is being stopped in "
|
Zotero.debug(this.Type + " queue for library " + this.libraryID
|
||||||
+ "Zotero.Sync.Storage.Queue.advance()", 2);
|
+ "is being stopped in Zotero.Sync.Storage.Queue.advance()", 2);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!this.queuedRequests) {
|
if (!this.queuedRequests) {
|
||||||
Zotero.debug("No remaining requests in " + this.name + " queue ("
|
Zotero.debug("No remaining requests in " + this.type
|
||||||
|
+ " queue for library " + this.libraryID + " ("
|
||||||
+ this.activeRequests + " active, "
|
+ this.activeRequests + " active, "
|
||||||
+ this.finishedRequests + " finished)");
|
+ this.finishedRequests + " finished)");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.activeRequests >= this.maxConcurrentRequests) {
|
if (this.activeRequests >= this.maxConcurrentRequests) {
|
||||||
Zotero.debug(this.Name + " queue is busy ("
|
Zotero.debug(this.Type + " queue for library " + this.libraryID
|
||||||
+ this.activeRequests + "/" + this.maxConcurrentRequests + ")");
|
+ " is busy (" + this.activeRequests + "/"
|
||||||
|
+ this.maxConcurrentRequests + ")");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Start the first unprocessed request
|
// Start the first unprocessed request
|
||||||
|
|
||||||
// Try the high-priority queue first
|
// Try the high-priority queue first
|
||||||
var name, request;
|
var self = this;
|
||||||
|
var request, name;
|
||||||
while (name = this._highPriority.shift()) {
|
while (name = this._highPriority.shift()) {
|
||||||
request = this._requests[name];
|
request = this._requests[name];
|
||||||
if (!request.isRunning() && !request.isFinished()) {
|
if (request.isRunning() || request.isFinished()) {
|
||||||
request.start();
|
continue;
|
||||||
this.advance();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requestName = name;
|
||||||
|
|
||||||
|
Q.fcall(function () {
|
||||||
|
var promise = request.start();
|
||||||
|
self.advance();
|
||||||
|
return promise;
|
||||||
|
})
|
||||||
|
.then(function (result) {
|
||||||
|
if (result.localChanges) {
|
||||||
|
self._localChanges = true;
|
||||||
|
}
|
||||||
|
if (result.remoteChanges) {
|
||||||
|
self._remoteChanges = true;
|
||||||
|
}
|
||||||
|
if (result.conflict) {
|
||||||
|
self.addConflict(
|
||||||
|
requestName,
|
||||||
|
result.conflict.local,
|
||||||
|
result.conflict.remote
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function (e) {
|
||||||
|
self.error(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// And then others
|
// And then others
|
||||||
for each(request in this._requests) {
|
for each(var request in this._requests) {
|
||||||
if (!request.isRunning() && !request.isFinished()) {
|
if (request.isRunning() || request.isFinished()) {
|
||||||
request.start();
|
continue;
|
||||||
this.advance();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let requestName = request.name;
|
||||||
|
|
||||||
|
// This isn't in an fcall() because the request needs to get marked
|
||||||
|
// as running immediately so that it doesn't get run again by a
|
||||||
|
// subsequent advance() call.
|
||||||
|
try {
|
||||||
|
var promise = request.start();
|
||||||
|
self.advance();
|
||||||
|
}
|
||||||
|
catch (e) {
|
||||||
|
self.error(e);
|
||||||
|
}
|
||||||
|
|
||||||
|
Q.when(promise)
|
||||||
|
.then(function (result) {
|
||||||
|
if (result.localChanges) {
|
||||||
|
self._localChanges = true;
|
||||||
|
}
|
||||||
|
if (result.remoteChanges) {
|
||||||
|
self._remoteChanges = true;
|
||||||
|
}
|
||||||
|
if (result.conflict) {
|
||||||
|
self.addConflict(
|
||||||
|
requestName,
|
||||||
|
result.conflict.local,
|
||||||
|
result.conflict.remote
|
||||||
|
);
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function (e) {
|
||||||
|
self.error(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -225,8 +344,26 @@ Zotero.Sync.Storage.Queue.prototype.updateProgress = function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Sync.Storage.Queue.prototype.addConflict = function (requestName, localData, remoteData) {
|
||||||
|
Zotero.debug('===========');
|
||||||
|
Zotero.debug(localData);
|
||||||
|
Zotero.debug(remoteData);
|
||||||
|
|
||||||
|
this._conflicts.push({
|
||||||
|
name: requestName,
|
||||||
|
localData: localData,
|
||||||
|
remoteData: remoteData
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.Queue.prototype.error = function (e) {
|
Zotero.Sync.Storage.Queue.prototype.error = function (e) {
|
||||||
Zotero.Sync.Storage.EventManager.error(e);
|
if (!this._error) {
|
||||||
|
this._error = e;
|
||||||
|
}
|
||||||
|
Zotero.debug(e, 1);
|
||||||
|
Components.utils.reportError(e.message ? e.message : e);
|
||||||
|
this.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -235,14 +372,18 @@ Zotero.Sync.Storage.Queue.prototype.error = function (e) {
|
||||||
*/
|
*/
|
||||||
Zotero.Sync.Storage.Queue.prototype.stop = function () {
|
Zotero.Sync.Storage.Queue.prototype.stop = function () {
|
||||||
if (!this._running) {
|
if (!this._running) {
|
||||||
Zotero.debug(this.Name + " queue is not running");
|
Zotero.debug(this.Type + " queue for library " + this.libraryID
|
||||||
|
+ " is not running");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (this._stopping) {
|
if (this._stopping) {
|
||||||
Zotero.debug("Already stopping " + this.name + " queue");
|
Zotero.debug("Already stopping " + this.type + " queue for library "
|
||||||
|
+ this.libraryID);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.debug("Stopping " + this.type + " queue for library " + this.libraryID);
|
||||||
|
|
||||||
// If no requests, finish manually
|
// If no requests, finish manually
|
||||||
/*if (this.activeRequests == 0) {
|
/*if (this.activeRequests == 0) {
|
||||||
this._finishedRequests = this._finishedRequests;
|
this._finishedRequests = this._finishedRequests;
|
||||||
|
@ -255,4 +396,13 @@ Zotero.Sync.Storage.Queue.prototype.stop = function () {
|
||||||
request.stop();
|
request.stop();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.debug("Queue is stopped");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Sync.Storage.Queue.prototype.reset = function () {
|
||||||
|
this._finished = false;
|
||||||
|
this._finishedReqs = 0;
|
||||||
|
this.totalRequests = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,8 +26,63 @@
|
||||||
|
|
||||||
Zotero.Sync.Storage.QueueManager = new function () {
|
Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
var _queues = {};
|
var _queues = {};
|
||||||
var _conflicts = [];
|
var _currentQueues = [];
|
||||||
var _cancelled = false;
|
|
||||||
|
this.start = function (libraryID) {
|
||||||
|
if (libraryID === 0 || libraryID) {
|
||||||
|
var queues = this.getAll(libraryID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var queues = this.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
Zotero.debug("Starting file sync queues");
|
||||||
|
|
||||||
|
var promises = [];
|
||||||
|
for each(var queue in queues) {
|
||||||
|
if (!queue.unfinishedRequests) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
Zotero.debug("Starting queue " + queue.name);
|
||||||
|
promises.push(queue.start());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!promises.length) {
|
||||||
|
Zotero.debug("No files to sync");
|
||||||
|
}
|
||||||
|
|
||||||
|
return Q.allResolved(promises)
|
||||||
|
.then(function (promises) {
|
||||||
|
Zotero.debug("All storage queues are finished");
|
||||||
|
promises.forEach(function (promise) {
|
||||||
|
if (promise.isFulfilled()) {
|
||||||
|
var result = promise.valueOf();
|
||||||
|
if (result.conflicts.length) {
|
||||||
|
Zotero.debug("Reconciling conflicts for library " + result.libraryID);
|
||||||
|
Zotero.debug(result.conflicts);
|
||||||
|
var data = _reconcileConflicts(result.conflicts);
|
||||||
|
if (data) {
|
||||||
|
_processMergeData(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return promises;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
this.stop = function (libraryID) {
|
||||||
|
if (libraryID === 0 || libraryID) {
|
||||||
|
var queues = this.getAll(libraryID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var queues = this.getAll();
|
||||||
|
}
|
||||||
|
for (var queue in queues) {
|
||||||
|
queue.stop();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -35,13 +90,19 @@ Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
*
|
*
|
||||||
* @param {String} queueName
|
* @param {String} queueName
|
||||||
*/
|
*/
|
||||||
this.get = function (queueName, noInit) {
|
this.get = function (queueName, libraryID, noInit) {
|
||||||
|
if (typeof libraryID == 'undefined') {
|
||||||
|
throw new Error("libraryID not specified");
|
||||||
|
}
|
||||||
|
|
||||||
|
var hash = queueName + "/" + libraryID;
|
||||||
|
|
||||||
// Initialize the queue if it doesn't exist yet
|
// Initialize the queue if it doesn't exist yet
|
||||||
if (!_queues[queueName]) {
|
if (!_queues[hash]) {
|
||||||
if (noInit) {
|
if (noInit) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
var queue = new Zotero.Sync.Storage.Queue(queueName);
|
var queue = new Zotero.Sync.Storage.Queue(queueName, libraryID);
|
||||||
switch (queueName) {
|
switch (queueName) {
|
||||||
case 'download':
|
case 'download':
|
||||||
queue.maxConcurrentRequests =
|
queue.maxConcurrentRequests =
|
||||||
|
@ -56,22 +117,36 @@ Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
default:
|
default:
|
||||||
throw ("Invalid queue '" + queueName + "' in Zotero.Sync.Storage.QueueManager.get()");
|
throw ("Invalid queue '" + queueName + "' in Zotero.Sync.Storage.QueueManager.get()");
|
||||||
}
|
}
|
||||||
_queues[queueName] = queue;
|
_queues[hash] = queue;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _queues[queueName];
|
return _queues[hash];
|
||||||
}
|
};
|
||||||
|
|
||||||
|
|
||||||
this.getAll = function () {
|
this.getAll = function (libraryID) {
|
||||||
var queues = [];
|
var queues = [];
|
||||||
for each(var queue in _queues) {
|
for each(var queue in _queues) {
|
||||||
queues.push(queue);
|
if (typeof libraryID == 'undefined' || queue.libraryID === libraryID) {
|
||||||
|
queues.push(queue);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return queues;
|
return queues;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
this.addCurrentQueue = function (queue) {
|
||||||
|
if (!this.hasCurrentQueue(queue)) {
|
||||||
|
_currentQueues.push(queue.name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.hasCurrentQueue = function (queue) {
|
||||||
|
return _currentQueues.indexOf(queue.name) != -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop all queues
|
* Stop all queues
|
||||||
*
|
*
|
||||||
|
@ -81,7 +156,6 @@ Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
*/
|
*/
|
||||||
this.cancel = function (skipStorageFinish) {
|
this.cancel = function (skipStorageFinish) {
|
||||||
Zotero.debug("Stopping all storage queues");
|
Zotero.debug("Stopping all storage queues");
|
||||||
_cancelled = true;
|
|
||||||
for each(var queue in _queues) {
|
for each(var queue in _queues) {
|
||||||
if (queue.isRunning() && !queue.isStopping()) {
|
if (queue.isRunning() && !queue.isStopping()) {
|
||||||
queue.stop();
|
queue.stop();
|
||||||
|
@ -92,26 +166,7 @@ Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
|
|
||||||
this.finish = function () {
|
this.finish = function () {
|
||||||
Zotero.debug("All storage queues are finished");
|
Zotero.debug("All storage queues are finished");
|
||||||
|
_currentQueues = [];
|
||||||
if (!_cancelled && _conflicts.length) {
|
|
||||||
var data = _reconcileConflicts();
|
|
||||||
if (data) {
|
|
||||||
_processMergeData(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (_cancelled) {
|
|
||||||
Zotero.Sync.Storage.EventManager.stop();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Zotero.Sync.Storage.EventManager.success();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
_cancelled = false;
|
|
||||||
_conflicts = [];
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -132,86 +187,32 @@ Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
activeRequests += queue.activeRequests;
|
activeRequests += queue.activeRequests;
|
||||||
}
|
}
|
||||||
if (activeRequests == 0) {
|
if (activeRequests == 0) {
|
||||||
this.updateProgressMeters(0);
|
_updateProgressMeters(0);
|
||||||
if (allFinished) {
|
if (allFinished) {
|
||||||
this.finish();
|
this.finish();
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Percentage
|
var status = {};
|
||||||
var percentageSum = 0;
|
|
||||||
var numQueues = 0;
|
|
||||||
for each(var queue in _queues) {
|
for each(var queue in _queues) {
|
||||||
percentageSum += queue.percentage;
|
if (!this.hasCurrentQueue(queue)) {
|
||||||
numQueues++;
|
|
||||||
}
|
|
||||||
var percentage = Math.round(percentageSum / numQueues);
|
|
||||||
//Zotero.debug("Total percentage is " + percentage);
|
|
||||||
|
|
||||||
// Remaining KB
|
|
||||||
var downloadStatus = _queues.download ?
|
|
||||||
_getQueueStatus(_queues.download) : 0;
|
|
||||||
var uploadStatus = _queues.upload ?
|
|
||||||
_getQueueStatus(_queues.upload) : 0;
|
|
||||||
|
|
||||||
this.updateProgressMeters(
|
|
||||||
activeRequests, percentage, downloadStatus, uploadStatus
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Cycle through windows, updating progress meters with new values
|
|
||||||
*/
|
|
||||||
this.updateProgressMeters = function (activeRequests, percentage, downloadStatus, uploadStatus) {
|
|
||||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
||||||
.getService(Components.interfaces.nsIWindowMediator);
|
|
||||||
var enumerator = wm.getEnumerator("navigator:browser");
|
|
||||||
while (enumerator.hasMoreElements()) {
|
|
||||||
var win = enumerator.getNext();
|
|
||||||
if (!win.ZoteroPane) continue;
|
|
||||||
var doc = win.ZoteroPane.document;
|
|
||||||
|
|
||||||
//
|
|
||||||
// TODO: Move to overlay.js?
|
|
||||||
//
|
|
||||||
var box = doc.getElementById("zotero-tb-sync-progress-box");
|
|
||||||
var meter = doc.getElementById("zotero-tb-sync-progress");
|
|
||||||
|
|
||||||
if (activeRequests == 0) {
|
|
||||||
box.hidden = true;
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
meter.setAttribute("value", percentage);
|
if (!status[queue.libraryID]) {
|
||||||
box.hidden = false;
|
status[queue.libraryID] = {};
|
||||||
|
}
|
||||||
var tooltip = doc.
|
if (!status[queue.libraryID][queue.type]) {
|
||||||
getElementById("zotero-tb-sync-progress-tooltip-progress");
|
status[queue.libraryID][queue.type] = {};
|
||||||
tooltip.setAttribute("value", percentage + "%");
|
}
|
||||||
|
status[queue.libraryID][queue.type].statusString = _getQueueStatus(queue);
|
||||||
var tooltip = doc.
|
status[queue.libraryID][queue.type].percentage = queue.percentage;
|
||||||
getElementById("zotero-tb-sync-progress-tooltip-downloads");
|
status[queue.libraryID][queue.type].totalRequests = queue.totalRequests;
|
||||||
tooltip.setAttribute("value", downloadStatus);
|
status[queue.libraryID][queue.type].finished = queue.finished;
|
||||||
|
|
||||||
var tooltip = doc.
|
|
||||||
getElementById("zotero-tb-sync-progress-tooltip-uploads");
|
|
||||||
tooltip.setAttribute("value", uploadStatus);
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
this.addConflict = function (requestName, localData, remoteData) {
|
|
||||||
Zotero.debug('===========');
|
|
||||||
Zotero.debug(localData);
|
|
||||||
Zotero.debug(remoteData);
|
|
||||||
|
|
||||||
_conflicts.push({
|
_updateProgressMeters(activeRequests, status);
|
||||||
name: requestName,
|
|
||||||
localData: localData,
|
|
||||||
remoteData: remoteData
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -226,26 +227,76 @@ Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
var unfinishedRequests = queue.unfinishedRequests;
|
var unfinishedRequests = queue.unfinishedRequests;
|
||||||
|
|
||||||
if (!unfinishedRequests) {
|
if (!unfinishedRequests) {
|
||||||
return Zotero.getString('sync.storage.none')
|
return Zotero.getString('sync.storage.none');
|
||||||
}
|
}
|
||||||
|
|
||||||
var kbRemaining = Zotero.getString(
|
if (remaining > 1000) {
|
||||||
'sync.storage.kbRemaining',
|
var bytesRemaining = Zotero.getString(
|
||||||
Zotero.Utilities.numberFormat(remaining / 1024, 0)
|
'sync.storage.mbRemaining',
|
||||||
);
|
Zotero.Utilities.numberFormat(remaining / 1000 / 1000, 1)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var bytesRemaining = Zotero.getString(
|
||||||
|
'sync.storage.kbRemaining',
|
||||||
|
Zotero.Utilities.numberFormat(remaining / 1000, 0)
|
||||||
|
);
|
||||||
|
}
|
||||||
var totalRequests = queue.totalRequests;
|
var totalRequests = queue.totalRequests;
|
||||||
var filesRemaining = Zotero.getString(
|
var filesRemaining = Zotero.getString(
|
||||||
'sync.storage.filesRemaining',
|
'sync.storage.filesRemaining',
|
||||||
[totalRequests - unfinishedRequests, totalRequests]
|
[totalRequests - unfinishedRequests, totalRequests]
|
||||||
);
|
);
|
||||||
var status = Zotero.localeJoin([kbRemaining, '(' + filesRemaining + ')']);
|
return bytesRemaining + ' (' + filesRemaining + ')';
|
||||||
return status;
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cycle through windows, updating progress meters with new values
|
||||||
|
*/
|
||||||
|
function _updateProgressMeters(activeRequests, status) {
|
||||||
|
// Get overall percentage across queues
|
||||||
|
var sum = 0, num = 0, percentage, total;
|
||||||
|
for each(var libraryStatus in status) {
|
||||||
|
for each(var queueStatus in libraryStatus) {
|
||||||
|
percentage = queueStatus.percentage;
|
||||||
|
total = queueStatus.totalRequests;
|
||||||
|
sum += total * percentage;
|
||||||
|
num += total;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var percentage = Math.round(sum / num);
|
||||||
|
|
||||||
|
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
||||||
|
.getService(Components.interfaces.nsIWindowMediator);
|
||||||
|
var enumerator = wm.getEnumerator("navigator:browser");
|
||||||
|
while (enumerator.hasMoreElements()) {
|
||||||
|
var win = enumerator.getNext();
|
||||||
|
if (!win.ZoteroPane) continue;
|
||||||
|
var doc = win.ZoteroPane.document;
|
||||||
|
|
||||||
|
var box = doc.getElementById("zotero-tb-sync-progress-box");
|
||||||
|
var meter = doc.getElementById("zotero-tb-sync-progress");
|
||||||
|
|
||||||
|
if (activeRequests == 0) {
|
||||||
|
box.hidden = true;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
meter.setAttribute("value", percentage);
|
||||||
|
box.hidden = false;
|
||||||
|
|
||||||
|
var percentageLabel = doc.getElementById('zotero-tb-sync-progress-tooltip-progress');
|
||||||
|
percentageLabel.lastChild.setAttribute('value', percentage + "%");
|
||||||
|
|
||||||
|
var statusBox = doc.getElementById('zotero-tb-sync-progress-status');
|
||||||
|
statusBox.data = status;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function _reconcileConflicts() {
|
function _reconcileConflicts(conflicts) {
|
||||||
var objectPairs = [];
|
var objectPairs = [];
|
||||||
for each(var conflict in _conflicts) {
|
for each(var conflict in conflicts) {
|
||||||
var item = Zotero.Sync.Storage.getItemFromRequestName(conflict.name);
|
var item = Zotero.Sync.Storage.getItemFromRequestName(conflict.name);
|
||||||
var item1 = item.clone(false, false, true);
|
var item1 = item.clone(false, false, true);
|
||||||
item1.setField('dateModified',
|
item1.setField('dateModified',
|
||||||
|
@ -279,8 +330,8 @@ Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
|
|
||||||
// Since we're only putting cloned items into the merge window,
|
// Since we're only putting cloned items into the merge window,
|
||||||
// we have to manually set the ids
|
// we have to manually set the ids
|
||||||
for (var i=0; i<_conflicts.length; i++) {
|
for (var i=0; i<conflicts.length; i++) {
|
||||||
io.dataOut[i].id = Zotero.Sync.Storage.getItemFromRequestName(_conflicts[i].name).id;
|
io.dataOut[i].id = Zotero.Sync.Storage.getItemFromRequestName(conflicts[i].name).id;
|
||||||
}
|
}
|
||||||
|
|
||||||
return io.dataOut;
|
return io.dataOut;
|
||||||
|
@ -292,8 +343,6 @@ Zotero.Sync.Storage.QueueManager = new function () {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Sync.Storage.resyncOnFinish = true;
|
|
||||||
|
|
||||||
for each(var mergeItem in data) {
|
for each(var mergeItem in data) {
|
||||||
var itemID = mergeItem.id;
|
var itemID = mergeItem.id;
|
||||||
var dateModified = mergeItem.ref.getField('dateModified');
|
var dateModified = mergeItem.ref.getField('dateModified');
|
||||||
|
|
|
@ -29,12 +29,11 @@
|
||||||
*
|
*
|
||||||
* @param {String} name Identifier for request (e.g., "[libraryID]/[key]")
|
* @param {String} name Identifier for request (e.g., "[libraryID]/[key]")
|
||||||
* @param {Function} onStart Callback to run when request starts
|
* @param {Function} onStart Callback to run when request starts
|
||||||
* @param {Function} onStop Callback to run when request stops
|
|
||||||
*/
|
*/
|
||||||
Zotero.Sync.Storage.Request = function (name, callbacks) {
|
Zotero.Sync.Storage.Request = function (name, callbacks) {
|
||||||
Zotero.debug("Initializing request '" + name + "'");
|
//Zotero.debug("Initializing request '" + name + "'");
|
||||||
|
|
||||||
this.callbacks = ['onStart', 'onProgress', 'onStop'];
|
this.callbacks = ['onStart', 'onProgress'];
|
||||||
|
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.channel = null;
|
this.channel = null;
|
||||||
|
@ -42,10 +41,13 @@ Zotero.Sync.Storage.Request = function (name, callbacks) {
|
||||||
this.progress = 0;
|
this.progress = 0;
|
||||||
this.progressMax = 0;
|
this.progressMax = 0;
|
||||||
|
|
||||||
|
this._deferred = Q.defer();
|
||||||
this._running = false;
|
this._running = false;
|
||||||
this._percentage = 0;
|
this._percentage = 0;
|
||||||
this._remaining = null;
|
this._remaining = null;
|
||||||
|
this._maxSize = null;
|
||||||
this._finished = false;
|
this._finished = false;
|
||||||
|
this._changesMade = false;
|
||||||
|
|
||||||
for (var func in callbacks) {
|
for (var func in callbacks) {
|
||||||
if (this.callbacks.indexOf(func) !== -1) {
|
if (this.callbacks.indexOf(func) !== -1) {
|
||||||
|
@ -59,6 +61,11 @@ Zotero.Sync.Storage.Request = function (name, callbacks) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Sync.Storage.Request.prototype.setMaxSize = function (size) {
|
||||||
|
this._maxSize = size;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add callbacks from another request to this request
|
* Add callbacks from another request to this request
|
||||||
*/
|
*/
|
||||||
|
@ -90,7 +97,16 @@ Zotero.Sync.Storage.Request.prototype.importCallbacks = function (request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Sync.Storage.Request.prototype.__defineGetter__('promise', function () {
|
||||||
|
return this._deferred.promise;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.Request.prototype.__defineGetter__('percentage', function () {
|
Zotero.Sync.Storage.Request.prototype.__defineGetter__('percentage', function () {
|
||||||
|
if (this._finished) {
|
||||||
|
return 100;
|
||||||
|
}
|
||||||
|
|
||||||
if (this.progressMax == 0) {
|
if (this.progressMax == 0) {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -98,14 +114,14 @@ Zotero.Sync.Storage.Request.prototype.__defineGetter__('percentage', function ()
|
||||||
var percentage = Math.round((this.progress / this.progressMax) * 100);
|
var percentage = Math.round((this.progress / this.progressMax) * 100);
|
||||||
if (percentage < this._percentage) {
|
if (percentage < this._percentage) {
|
||||||
Zotero.debug(percentage + " is less than last percentage of "
|
Zotero.debug(percentage + " is less than last percentage of "
|
||||||
+ this._percentage + " for request '" + this.name + "'", 2);
|
+ this._percentage + " for request " + this.name, 2);
|
||||||
Zotero.debug(this.progress);
|
Zotero.debug(this.progress);
|
||||||
Zotero.debug(this.progressMax);
|
Zotero.debug(this.progressMax);
|
||||||
percentage = this._percentage;
|
percentage = this._percentage;
|
||||||
}
|
}
|
||||||
else if (percentage > 100) {
|
else if (percentage > 100) {
|
||||||
Zotero.debug(percentage + " is greater than 100 for "
|
Zotero.debug(percentage + " is greater than 100 for "
|
||||||
+ this.name + " request", 2);
|
+ "request " + this.name, 2);
|
||||||
Zotero.debug(this.progress);
|
Zotero.debug(this.progress);
|
||||||
Zotero.debug(this.progressMax);
|
Zotero.debug(this.progressMax);
|
||||||
percentage = 100;
|
percentage = 100;
|
||||||
|
@ -119,7 +135,15 @@ Zotero.Sync.Storage.Request.prototype.__defineGetter__('percentage', function ()
|
||||||
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.Request.prototype.__defineGetter__('remaining', function () {
|
Zotero.Sync.Storage.Request.prototype.__defineGetter__('remaining', function () {
|
||||||
|
if (this._finished) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (!this.progressMax) {
|
if (!this.progressMax) {
|
||||||
|
if (this.queue.type == 'upload' && this._maxSize) {
|
||||||
|
return Math.round(Zotero.Sync.Storage.compressionTracker.ratio * this._maxSize);
|
||||||
|
}
|
||||||
|
|
||||||
//Zotero.debug("Remaining not yet available for request '" + this.name + "'");
|
//Zotero.debug("Remaining not yet available for request '" + this.name + "'");
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -151,22 +175,70 @@ Zotero.Sync.Storage.Request.prototype.setChannel = function (channel) {
|
||||||
|
|
||||||
Zotero.Sync.Storage.Request.prototype.start = function () {
|
Zotero.Sync.Storage.Request.prototype.start = function () {
|
||||||
if (!this.queue) {
|
if (!this.queue) {
|
||||||
throw ("Request '" + this.name + "' must be added to a queue before starting");
|
throw ("Request " + this.name + " must be added to a queue before starting");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Zotero.debug("Starting " + this.queue.name + " request " + this.name);
|
||||||
|
|
||||||
if (this._running) {
|
if (this._running) {
|
||||||
throw ("Request '" + this.name + "' already running in "
|
throw new Error("Request " + this.name + " already running");
|
||||||
+ "Zotero.Sync.Storage.Request.start()");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.debug("Starting " + this.queue.name + " request '" + this.name + "'");
|
|
||||||
this._running = true;
|
this._running = true;
|
||||||
this.queue.activeRequests++;
|
this.queue.activeRequests++;
|
||||||
if (this._onStart) {
|
|
||||||
for each(var f in this._onStart) {
|
if (this.queue.type == 'download') {
|
||||||
f(this);
|
Zotero.Sync.Storage.setItemDownloadPercentage(this.name, 0);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
// this._onStart is an array of promises returning changesMade.
|
||||||
|
//
|
||||||
|
// The main sync logic is triggered here.
|
||||||
|
|
||||||
|
Q.all([f(this) for each(f in this._onStart)])
|
||||||
|
.then(function (results) {
|
||||||
|
return {
|
||||||
|
localChanges: results.some(function (val) val && val.localChanges == true),
|
||||||
|
remoteChanges: results.some(function (val) val && val.remoteChanges == true),
|
||||||
|
conflict: results.reduce(function (prev, cur) {
|
||||||
|
return prev.conflict ? prev : cur;
|
||||||
|
}).conflict
|
||||||
|
};
|
||||||
|
})
|
||||||
|
.then(function (results) {
|
||||||
|
Zotero.debug('!!!!');
|
||||||
|
Zotero.debug(results);
|
||||||
|
|
||||||
|
if (results.localChanges) {
|
||||||
|
Zotero.debug("Changes were made by " + self.queue.name
|
||||||
|
+ " request " + self.name);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug("No changes were made by " + self.queue.name
|
||||||
|
+ " request " + self.name);
|
||||||
|
}
|
||||||
|
|
||||||
|
// This promise updates localChanges/remoteChanges on the queue
|
||||||
|
self._deferred.resolve(results);
|
||||||
|
})
|
||||||
|
.fail(function (e) {
|
||||||
|
Zotero.debug(self.queue.Type + " request " + self.name + " failed");
|
||||||
|
Zotero.debug(self._deferred);
|
||||||
|
Zotero.debug(self._deferred.promise.isFulfilled());
|
||||||
|
self._deferred.reject(e);
|
||||||
|
Zotero.debug(self._deferred.promise.isFulfilled());
|
||||||
|
Zotero.debug(self._deferred.promise.isRejected());
|
||||||
|
})
|
||||||
|
// Finish the request (and in turn the queue, if this is the last request)
|
||||||
|
.fin(function () {
|
||||||
|
if (!self._finished) {
|
||||||
|
self._finish();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return this._deferred.promise;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -191,6 +263,8 @@ Zotero.Sync.Storage.Request.prototype.isFinished = function () {
|
||||||
* (usually total bytes)
|
* (usually total bytes)
|
||||||
*/
|
*/
|
||||||
Zotero.Sync.Storage.Request.prototype.onProgress = function (channel, progress, progressMax) {
|
Zotero.Sync.Storage.Request.prototype.onProgress = function (channel, progress, progressMax) {
|
||||||
|
Zotero.debug(progress + "/" + progressMax + " for request " + this.name);
|
||||||
|
|
||||||
if (!this._running) {
|
if (!this._running) {
|
||||||
Zotero.debug("Trying to update finished request " + this.name + " in "
|
Zotero.debug("Trying to update finished request " + this.name + " in "
|
||||||
+ "Zotero.Sync.Storage.Request.onProgress() "
|
+ "Zotero.Sync.Storage.Request.onProgress() "
|
||||||
|
@ -219,6 +293,10 @@ Zotero.Sync.Storage.Request.prototype.onProgress = function (channel, progress,
|
||||||
this.progressMax = progressMax;
|
this.progressMax = progressMax;
|
||||||
this.queue.updateProgress();
|
this.queue.updateProgress();
|
||||||
|
|
||||||
|
if (this.queue.type == 'download') {
|
||||||
|
Zotero.Sync.Storage.setItemDownloadPercentage(this.name, this.percentage);
|
||||||
|
}
|
||||||
|
|
||||||
if (this.onProgress) {
|
if (this.onProgress) {
|
||||||
for each(var f in this._onProgress) {
|
for each(var f in this._onProgress) {
|
||||||
f(progress, progressMax);
|
f(progress, progressMax);
|
||||||
|
@ -227,62 +305,48 @@ Zotero.Sync.Storage.Request.prototype.onProgress = function (channel, progress,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.Request.prototype.error = function (e) {
|
|
||||||
this.queue.error(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stop the request's underlying network request, if there is one
|
* Stop the request's underlying network request, if there is one
|
||||||
*/
|
*/
|
||||||
Zotero.Sync.Storage.Request.prototype.stop = function () {
|
Zotero.Sync.Storage.Request.prototype.stop = function () {
|
||||||
var finishNow = false;
|
if (this.channel) {
|
||||||
try {
|
try {
|
||||||
// If upload already finished, finish() will never be called otherwise
|
Zotero.debug("Stopping request '" + this.name + "'");
|
||||||
if (this.channel) {
|
this.channel.cancel(0x804b0002); // NS_BINDING_ABORTED
|
||||||
this.channel.QueryInterface(Components.interfaces.nsIHttpChannel);
|
}
|
||||||
// Throws error if request not finished
|
catch (e) {
|
||||||
this.channel.requestSucceeded;
|
Zotero.debug(e);
|
||||||
Zotero.debug("Channel is no longer running for request " + this.name);
|
|
||||||
Zotero.debug(this.channel.requestSucceeded);
|
|
||||||
finishNow = true;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {}
|
else {
|
||||||
|
this._finish();
|
||||||
if (!this._running || !this.channel || finishNow) {
|
|
||||||
this.finish();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.debug("Stopping request '" + this.name + "'");
|
|
||||||
this.channel.cancel(0x804b0002); // NS_BINDING_ABORTED
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mark request as finished and notify queue that it's done
|
* Mark request as finished and notify queue that it's done
|
||||||
*/
|
*/
|
||||||
Zotero.Sync.Storage.Request.prototype.finish = function () {
|
Zotero.Sync.Storage.Request.prototype._finish = function () {
|
||||||
if (this._finished) {
|
|
||||||
throw ("Request '" + this.name + "' is already finished");
|
|
||||||
}
|
|
||||||
|
|
||||||
Zotero.debug("Finishing " + this.queue.name + " request '" + this.name + "'");
|
Zotero.debug("Finishing " + this.queue.name + " request '" + this.name + "'");
|
||||||
this._finished = true;
|
this._finished = true;
|
||||||
var active = this._running;
|
var active = this._running;
|
||||||
this._running = false;
|
this._running = false;
|
||||||
|
|
||||||
|
Zotero.Sync.Storage.setItemDownloadPercentage(this.name, false);
|
||||||
|
|
||||||
if (active) {
|
if (active) {
|
||||||
this.queue.activeRequests--;
|
this.queue.activeRequests--;
|
||||||
}
|
}
|
||||||
// mechanism for failures?
|
// TEMP: mechanism for failures?
|
||||||
this.queue.finishedRequests++;
|
try {
|
||||||
this.queue.updateProgress();
|
this.queue.finishedRequests++;
|
||||||
|
this.queue.updateProgress();
|
||||||
if (this._onStop) {
|
}
|
||||||
for each(var f in this._onStop) {
|
catch (e) {
|
||||||
f();
|
Zotero.debug(e);
|
||||||
}
|
Components.utils.reportError(e);
|
||||||
|
this._deferred.reject(e);
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
|
@ -505,12 +505,12 @@ Zotero.Sync.Runner = new function () {
|
||||||
|
|
||||||
var _autoSyncTimer;
|
var _autoSyncTimer;
|
||||||
var _queue;
|
var _queue;
|
||||||
var _running;
|
|
||||||
var _background;
|
var _background;
|
||||||
|
|
||||||
var _lastSyncStatus;
|
var _lastSyncStatus;
|
||||||
var _currentSyncStatusLabel;
|
var _currentSyncStatusLabel;
|
||||||
var _currentLastSyncLabel;
|
var _currentLastSyncLabel;
|
||||||
|
var _errorsByLibrary = {};
|
||||||
|
|
||||||
var _warning = null;
|
var _warning = null;
|
||||||
|
|
||||||
|
@ -526,16 +526,9 @@ Zotero.Sync.Runner = new function () {
|
||||||
this.clearSyncTimeout(); // DEBUG: necessary?
|
this.clearSyncTimeout(); // DEBUG: necessary?
|
||||||
var msg = "Zotero cannot sync while " + Zotero.appName + " is in offline mode.";
|
var msg = "Zotero cannot sync while " + Zotero.appName + " is in offline mode.";
|
||||||
var e = new Zotero.Error(msg, 0, { dialogButtonText: null })
|
var e = new Zotero.Error(msg, 0, { dialogButtonText: null })
|
||||||
this.setSyncIcon('error', e);
|
Components.utils.reportError(e);
|
||||||
return false;
|
Zotero.debug(e, 1);
|
||||||
}
|
this.setSyncIcon(e);
|
||||||
|
|
||||||
if (_running) {
|
|
||||||
// TODO: show status in all windows
|
|
||||||
var msg = "A sync process is already running. To view progress, check "
|
|
||||||
+ "the window in which the sync began or restart " + Zotero.appName + ".";
|
|
||||||
var e = new Zotero.Error(msg, 0, { dialogButtonText: null, frontWindowOnly: true })
|
|
||||||
this.setSyncIcon('error', e);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -543,7 +536,6 @@ Zotero.Sync.Runner = new function () {
|
||||||
Zotero.purgeDataObjects(true);
|
Zotero.purgeDataObjects(true);
|
||||||
|
|
||||||
_background = !!background;
|
_background = !!background;
|
||||||
_running = true;
|
|
||||||
this.setSyncIcon('animate');
|
this.setSyncIcon('animate');
|
||||||
|
|
||||||
var finalCallbacks = {
|
var finalCallbacks = {
|
||||||
|
@ -554,61 +546,30 @@ Zotero.Sync.Runner = new function () {
|
||||||
};
|
};
|
||||||
|
|
||||||
var storageSync = function () {
|
var storageSync = function () {
|
||||||
var syncNeeded = false;
|
|
||||||
|
|
||||||
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.syncingFiles'));
|
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.syncingFiles'));
|
||||||
|
|
||||||
var zfsSync = function (skipSyncNeeded) {
|
Zotero.Sync.Storage.sync()
|
||||||
Zotero.Sync.Storage.ZFS.sync({
|
.then(function (results) {
|
||||||
// ZFS success
|
Zotero.debug("File sync is finished");
|
||||||
onSuccess: function () {
|
|
||||||
setTimeout(function () {
|
|
||||||
Zotero.Sync.Server.sync(finalCallbacks);
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ZFS skip
|
|
||||||
onSkip: function () {
|
|
||||||
setTimeout(function () {
|
|
||||||
if (skipSyncNeeded) {
|
|
||||||
Zotero.Sync.Server.sync(finalCallbacks);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
Zotero.Sync.Runner.stop();
|
|
||||||
}
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ZFS cancel
|
|
||||||
onStop: function () {
|
|
||||||
setTimeout(function () {
|
|
||||||
Zotero.Sync.Runner.stop();
|
|
||||||
}, 0);
|
|
||||||
},
|
|
||||||
|
|
||||||
// ZFS failure
|
|
||||||
onError: Zotero.Sync.Runner.error,
|
|
||||||
|
|
||||||
onWarning: Zotero.Sync.Runner.warning
|
|
||||||
})
|
|
||||||
};
|
|
||||||
|
|
||||||
Zotero.Sync.Storage.WebDAV.sync({
|
|
||||||
// WebDAV success
|
|
||||||
onSuccess: function () {
|
|
||||||
zfsSync(true);
|
|
||||||
},
|
|
||||||
|
|
||||||
// WebDAV skip
|
if (results.errors.length) {
|
||||||
onSkip: function () {
|
Zotero.Sync.Runner.setErrors(results.errors);
|
||||||
zfsSync();
|
|
||||||
},
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// WebDAV cancel
|
if (results.changesMade) {
|
||||||
onStop: Zotero.Sync.Runner.stop,
|
Zotero.debug("Changes made during file sync "
|
||||||
|
+ "-- performing additional data sync");
|
||||||
// WebDAV failure
|
Zotero.Sync.Server.sync(finalCallbacks);
|
||||||
onError: Zotero.Sync.Runner.error
|
}
|
||||||
|
else {
|
||||||
|
Zotero.Sync.Runner.stop();
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.fail(function (e) {
|
||||||
|
Zotero.debug("File sync failed", 1);
|
||||||
|
Zotero.Sync.Runner.error(e);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -620,23 +581,26 @@ Zotero.Sync.Runner = new function () {
|
||||||
onSkip: storageSync,
|
onSkip: storageSync,
|
||||||
|
|
||||||
// Sync 1 stop
|
// Sync 1 stop
|
||||||
onStop: Zotero.Sync.Runner.stop,
|
onStop: function () {
|
||||||
|
Zotero.Sync.Runner.stop();
|
||||||
|
},
|
||||||
|
|
||||||
// Sync 1 error
|
// Sync 1 error
|
||||||
onError: Zotero.Sync.Runner.error
|
onError: function (e) {
|
||||||
|
Zotero.Sync.Runner.error(e);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.stop = function () {
|
this.stop = function () {
|
||||||
if (_warning) {
|
if (_warning) {
|
||||||
Zotero.Sync.Runner.setSyncIcon('warning', _warning);
|
Zotero.Sync.Runner.setSyncIcon(_warning);
|
||||||
_warning = null;
|
_warning = null;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
Zotero.Sync.Runner.setSyncIcon();
|
Zotero.Sync.Runner.setSyncIcon();
|
||||||
}
|
}
|
||||||
_running = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -644,14 +608,17 @@ Zotero.Sync.Runner = new function () {
|
||||||
* Log a warning, but don't throw an error
|
* Log a warning, but don't throw an error
|
||||||
*/
|
*/
|
||||||
this.warning = function (e) {
|
this.warning = function (e) {
|
||||||
|
Zotero.debug(e, 2);
|
||||||
Components.utils.reportError(e);
|
Components.utils.reportError(e);
|
||||||
|
e.status = 'warning';
|
||||||
_warning = e;
|
_warning = e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.error = function (e) {
|
this.error = function (e) {
|
||||||
Zotero.Sync.Runner.setSyncIcon('error', e);
|
Components.utils.reportError(e);
|
||||||
_running = false;
|
Zotero.debug(e, 1);
|
||||||
|
Zotero.Sync.Runner.setSyncIcon(e);
|
||||||
throw (e);
|
throw (e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -740,60 +707,85 @@ Zotero.Sync.Runner = new function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
this.setSyncIcon = function (status, e) {
|
/**
|
||||||
var message;
|
* Trigger updating of the main sync icon, the sync error icon, and
|
||||||
var buttonText;
|
* library-specific sync error icons across all windows
|
||||||
var buttonCallback;
|
*/
|
||||||
var frontWindowOnly = false;
|
this.setErrors = function (errors) {
|
||||||
|
Zotero.debug(errors);
|
||||||
|
errors = [this.parseSyncError(e) for each(e in errors)];
|
||||||
|
Zotero.debug(errors);
|
||||||
|
_errorsByLibrary = {};
|
||||||
|
|
||||||
status = status ? status : '';
|
var primaryError = this.getPrimaryError(errors);
|
||||||
|
Zotero.debug(primaryError);
|
||||||
|
this.setSyncIcon(primaryError);
|
||||||
|
|
||||||
switch (status) {
|
// Store other errors by libraryID to be shown in the source list
|
||||||
case '':
|
for each(var e in errors) {
|
||||||
case 'animate':
|
// Skip non-library-specific errors
|
||||||
case 'warning':
|
if (typeof e.libraryID == 'undefined') {
|
||||||
case 'error':
|
continue;
|
||||||
break;
|
}
|
||||||
|
|
||||||
default:
|
if (!_errorsByLibrary[e.libraryID]) {
|
||||||
throw ("Invalid sync icon status '" + status
|
_errorsByLibrary[e.libraryID] = [];
|
||||||
+ "' in Zotero.Sync.Runner.setSyncIcon()");
|
}
|
||||||
|
_errorsByLibrary[e.libraryID].push(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e) {
|
// Refresh source list
|
||||||
if (e.data) {
|
Zotero.Notifier.trigger('redraw', 'collection', []);
|
||||||
if (e.data.dialogText) {
|
}
|
||||||
message = e.data.dialogText;
|
|
||||||
}
|
|
||||||
if (typeof e.data.dialogButtonText != 'undefined') {
|
this.getErrors = function (libraryID) {
|
||||||
buttonText = e.data.dialogButtonText;
|
if (!_errorsByLibrary[libraryID]) {
|
||||||
buttonCallback = e.data.dialogButtonCallback;
|
return false;
|
||||||
}
|
}
|
||||||
if (e.data.frontWindowOnly) {
|
return _errorsByLibrary[libraryID];
|
||||||
frontWindowOnly = e.data.frontWindowOnly;
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
this.getPrimaryError = function (errors) {
|
||||||
|
errors = [this.parseSyncError(e) for each(e in errors)];
|
||||||
|
|
||||||
|
// Set highest priority error as the primary (sync error icon)
|
||||||
|
var statusPriorities = {
|
||||||
|
info: 1,
|
||||||
|
warning: 2,
|
||||||
|
error: 3,
|
||||||
|
upgrade: 4,
|
||||||
|
|
||||||
|
// Skip these
|
||||||
|
animate: -1
|
||||||
|
};
|
||||||
|
var primaryError = false;
|
||||||
|
for each(var error in errors) {
|
||||||
|
if (!error.status || statusPriorities[error.status] == -1) {
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
if (!message) {
|
if (!primaryError || statusPriorities[error.status]
|
||||||
if (e.message) {
|
> statusPriorities[primaryError.status]) {
|
||||||
message = e.message;
|
primaryError = error;
|
||||||
}
|
|
||||||
else {
|
|
||||||
message = e;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return primaryError;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the main sync error icon across all windows
|
||||||
|
*/
|
||||||
|
this.setSyncIcon = function (e) {
|
||||||
|
e = this.parseSyncError(e);
|
||||||
|
|
||||||
var upgradeRequired = false;
|
|
||||||
if (Zotero.Sync.Server.upgradeRequired) {
|
if (Zotero.Sync.Server.upgradeRequired) {
|
||||||
upgradeRequired = true;
|
e.status = 'upgrade';
|
||||||
Zotero.Sync.Server.upgradeRequired = false;
|
Zotero.Sync.Server.upgradeRequired = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == 'error') {
|
if (e.frontWindowOnly) {
|
||||||
var errorsLogged = Zotero.getErrors().length > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (frontWindowOnly) {
|
|
||||||
// Fake an nsISimpleEnumerator with just the topmost window
|
// Fake an nsISimpleEnumerator with just the topmost window
|
||||||
var enumerator = {
|
var enumerator = {
|
||||||
_returned: false,
|
_returned: false,
|
||||||
|
@ -820,100 +812,17 @@ Zotero.Sync.Runner = new function () {
|
||||||
|
|
||||||
while (enumerator.hasMoreElements()) {
|
while (enumerator.hasMoreElements()) {
|
||||||
var win = enumerator.getNext();
|
var win = enumerator.getNext();
|
||||||
if(!win.ZoteroPane) continue;
|
if (!win.ZoteroPane) continue;
|
||||||
var warning = win.ZoteroPane.document.getElementById('zotero-tb-sync-warning');
|
var doc = win.ZoteroPane.document;
|
||||||
var icon = win.ZoteroPane.document.getElementById('zotero-tb-sync');
|
|
||||||
|
|
||||||
if (status == 'warning' || status == 'error') {
|
var button = doc.getElementById('zotero-tb-sync-error');
|
||||||
icon.setAttribute('status', '');
|
this.setErrorIcon(button, [e]);
|
||||||
warning.hidden = false;
|
|
||||||
if (upgradeRequired) {
|
|
||||||
warning.setAttribute('mode', 'upgrade');
|
|
||||||
buttonText = null;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
warning.setAttribute('mode', status);
|
|
||||||
}
|
|
||||||
warning.tooltipText = message;
|
|
||||||
warning.onclick = function () {
|
|
||||||
var wm = Components.classes["@mozilla.org/appshell/window-mediator;1"]
|
|
||||||
.getService(Components.interfaces.nsIWindowMediator);
|
|
||||||
var win = wm.getMostRecentWindow("navigator:browser");
|
|
||||||
|
|
||||||
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
|
||||||
.getService(Components.interfaces.nsIPromptService);
|
|
||||||
// Warning
|
|
||||||
if (status == 'warning') {
|
|
||||||
var title = Zotero.getString('general.warning');
|
|
||||||
|
|
||||||
// If secondary button not specified, just use an alert
|
|
||||||
if (!buttonText) {
|
|
||||||
ps.alert(null, title, message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
|
|
||||||
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
|
|
||||||
var index = ps.confirmEx(
|
|
||||||
null,
|
|
||||||
title,
|
|
||||||
message,
|
|
||||||
buttonFlags,
|
|
||||||
"",
|
|
||||||
buttonText,
|
|
||||||
"", null, {}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (index == 1) {
|
|
||||||
setTimeout(function () { buttonCallback(); }, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Error
|
|
||||||
else if (status == 'error') {
|
|
||||||
// Probably not necessary, but let's be sure
|
|
||||||
if (!errorsLogged) {
|
|
||||||
Components.utils.reportError(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (typeof buttonText == 'undefined') {
|
|
||||||
buttonText = Zotero.getString('errorReport.reportError');
|
|
||||||
buttonCallback = function () {
|
|
||||||
win.ZoteroPane.reportErrors();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// If secondary button is explicitly null, just use an alert
|
|
||||||
else if (buttonText === null) {
|
|
||||||
ps.alert(null, title, message);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
|
|
||||||
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
|
|
||||||
var index = ps.confirmEx(
|
|
||||||
null,
|
|
||||||
Zotero.getString('general.error'),
|
|
||||||
message,
|
|
||||||
buttonFlags,
|
|
||||||
"",
|
|
||||||
buttonText,
|
|
||||||
"", null, {}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (index == 1) {
|
|
||||||
setTimeout(function () { buttonCallback(); }, 1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
icon.setAttribute('status', status);
|
|
||||||
warning.hidden = true;
|
|
||||||
warning.onclick = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
var syncIcon = doc.getElementById('zotero-tb-sync');
|
||||||
|
// Update sync icon state
|
||||||
|
syncIcon.setAttribute('status', e.status ? e.status : "");
|
||||||
// Disable button while spinning
|
// Disable button while spinning
|
||||||
icon.disabled = status == 'animate';
|
syncIcon.disabled = e.status == 'animate';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear status
|
// Clear status
|
||||||
|
@ -921,6 +830,9 @@ Zotero.Sync.Runner = new function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the sync icon tooltip message
|
||||||
|
*/
|
||||||
this.setSyncStatus = function (msg) {
|
this.setSyncStatus = function (msg) {
|
||||||
_lastSyncStatus = msg;
|
_lastSyncStatus = msg;
|
||||||
|
|
||||||
|
@ -931,6 +843,132 @@ Zotero.Sync.Runner = new function () {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.parseSyncError = function (e) {
|
||||||
|
if (!e) {
|
||||||
|
return { parsed: true };
|
||||||
|
}
|
||||||
|
|
||||||
|
var parsed = {
|
||||||
|
parsed: true
|
||||||
|
};
|
||||||
|
|
||||||
|
// In addition to actual errors, string states (e.g., 'animate')
|
||||||
|
// can be passed
|
||||||
|
if (typeof e == 'string') {
|
||||||
|
parsed.status = e;
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Already parsed
|
||||||
|
if (e.parsed) {
|
||||||
|
return e;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof e.libraryID != 'undefined') {
|
||||||
|
parsed.libraryID = e.libraryID;
|
||||||
|
}
|
||||||
|
parsed.status = e.status ? e.status : 'error';
|
||||||
|
|
||||||
|
if (e.data) {
|
||||||
|
if (e.data.dialogText) {
|
||||||
|
parsed.message = e.data.dialogText;
|
||||||
|
}
|
||||||
|
if (typeof e.data.dialogButtonText != 'undefined') {
|
||||||
|
parsed.buttonText = e.data.dialogButtonText;
|
||||||
|
parsed.buttonCallback = e.data.dialogButtonCallback;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!parsed.message) {
|
||||||
|
parsed.message = e.message ? e.message : e;
|
||||||
|
}
|
||||||
|
|
||||||
|
parsed.frontWindowOnly = !!(e && e.data && e.data.frontWindowOnly);
|
||||||
|
|
||||||
|
return parsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the state of the sync error icon and add an onclick to populate
|
||||||
|
* the error panel
|
||||||
|
*/
|
||||||
|
this.setErrorIcon = function (icon, errors) {
|
||||||
|
if (!errors || !errors.length) {
|
||||||
|
icon.hidden = true;
|
||||||
|
icon.onclick = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEMP: for now, use the first error
|
||||||
|
var e = this.getPrimaryError(errors);
|
||||||
|
|
||||||
|
if (!e.status) {
|
||||||
|
icon.hidden = true;
|
||||||
|
icon.onclick = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
icon.hidden = false;
|
||||||
|
icon.setAttribute('mode', e.status);
|
||||||
|
icon.onclick = function () {
|
||||||
|
var doc = this.ownerDocument;
|
||||||
|
|
||||||
|
var panel = Zotero.Sync.Runner.updateErrorPanel(doc, errors);
|
||||||
|
|
||||||
|
panel.openPopup(this, "after_end", 4, 0, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.updateErrorPanel = function (doc, errors) {
|
||||||
|
var panel = doc.getElementById('zotero-sync-error-panel');
|
||||||
|
var panelContent = doc.getElementById('zotero-sync-error-panel-content');
|
||||||
|
var panelButtons = doc.getElementById('zotero-sync-error-panel-buttons');
|
||||||
|
|
||||||
|
// Clear existing panel content
|
||||||
|
while (panelContent.hasChildNodes()) {
|
||||||
|
panelContent.removeChild(panelContent.firstChild);
|
||||||
|
}
|
||||||
|
while (panelButtons.hasChildNodes()) {
|
||||||
|
panelButtons.removeChild(panelButtons.firstChild);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TEMP: for now, we only show one error
|
||||||
|
var e = errors.concat().shift();
|
||||||
|
e = this.parseSyncError(e);
|
||||||
|
|
||||||
|
var desc = doc.createElement('description');
|
||||||
|
desc.textContent = e.message;
|
||||||
|
panelContent.appendChild(desc);
|
||||||
|
|
||||||
|
// If not an error and there's no explicit button text, don't show
|
||||||
|
// button to report errors
|
||||||
|
if (e.status != 'error' && typeof e.buttonText == 'undefined') {
|
||||||
|
e.buttonText = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (e.buttonText !== null) {
|
||||||
|
if (typeof e.buttonText == 'undefined') {
|
||||||
|
var buttonText = Zotero.getString('errorReport.reportError');
|
||||||
|
var buttonCallback = function () {
|
||||||
|
doc.defaultView.ZoteroPane.reportErrors();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var buttonText = e.buttonText;
|
||||||
|
var buttonCallback = e.buttonCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
var button = doc.createElement('button');
|
||||||
|
button.setAttribute('label', buttonText);
|
||||||
|
button.onclick = buttonCallback;
|
||||||
|
panelButtons.appendChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
return panel;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Register label in sync icon tooltip to receive updates
|
* Register label in sync icon tooltip to receive updates
|
||||||
*
|
*
|
||||||
|
@ -1440,7 +1478,7 @@ Zotero.Sync.Server = new function () {
|
||||||
Zotero.suppressUIUpdates = true;
|
Zotero.suppressUIUpdates = true;
|
||||||
_updatesInProgress = true;
|
_updatesInProgress = true;
|
||||||
|
|
||||||
var errorHandler = function (e) {
|
var errorHandler = function (e, rethrow) {
|
||||||
Zotero.DB.rollbackTransaction();
|
Zotero.DB.rollbackTransaction();
|
||||||
|
|
||||||
Zotero.UnresponsiveScriptIndicator.enable();
|
Zotero.UnresponsiveScriptIndicator.enable();
|
||||||
|
@ -1451,6 +1489,9 @@ Zotero.Sync.Server = new function () {
|
||||||
Zotero.suppressUIUpdates = false;
|
Zotero.suppressUIUpdates = false;
|
||||||
_updatesInProgress = false;
|
_updatesInProgress = false;
|
||||||
|
|
||||||
|
if (rethrow) {
|
||||||
|
throw (e);
|
||||||
|
}
|
||||||
_error(e);
|
_error(e);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1662,7 +1703,7 @@ Zotero.Sync.Server = new function () {
|
||||||
Zotero.pumpGenerator(gen, false, errorHandler);
|
Zotero.pumpGenerator(gen, false, errorHandler);
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
errorHandler(e);
|
errorHandler(e, true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
|
@ -2987,17 +3028,16 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
obj.attachmentSyncState =
|
obj.attachmentSyncState =
|
||||||
Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
|
Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
|
||||||
}
|
}
|
||||||
// Set existing attachments mtime update check
|
// Set existing attachments for mtime update check
|
||||||
else {
|
else {
|
||||||
var mtime = objectNode.getAttribute('storageModTime');
|
var mtime = objectNode.getAttribute('storageModTime');
|
||||||
if (mtime) {
|
if (mtime) {
|
||||||
var lk = Zotero.Items.getLibraryKeyHash(obj)
|
|
||||||
// Convert previously used Unix timestamps to ms-based timestamps
|
// Convert previously used Unix timestamps to ms-based timestamps
|
||||||
if (mtime < 10000000000) {
|
if (mtime < 10000000000) {
|
||||||
Zotero.debug("Converting Unix timestamp '" + mtime + "' to milliseconds");
|
Zotero.debug("Converting Unix timestamp '" + mtime + "' to milliseconds");
|
||||||
mtime = mtime * 1000;
|
mtime = mtime * 1000;
|
||||||
}
|
}
|
||||||
itemStorageModTimes[lk] = parseInt(mtime);
|
itemStorageModTimes[obj.id] = parseInt(mtime);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3313,18 +3353,8 @@ Zotero.Sync.Server.Data = new function() {
|
||||||
|
|
||||||
// Check mod times and hashes of updated items against stored values to see
|
// Check mod times and hashes of updated items against stored values to see
|
||||||
// if they've been updated elsewhere and mark for download if so
|
// if they've been updated elsewhere and mark for download if so
|
||||||
if (type == 'item') {
|
if (type == 'item' && Object.keys(itemStorageModTimes).length) {
|
||||||
var ids = [];
|
Zotero.Sync.Storage.checkForUpdatedFiles(itemStorageModTimes);
|
||||||
var modTimes = {};
|
|
||||||
for (var libraryKeyHash in itemStorageModTimes) {
|
|
||||||
var lk = Zotero.Items.parseLibraryKeyHash(libraryKeyHash);
|
|
||||||
var item = Zotero.Items.getByLibraryAndKey(lk.libraryID, lk.key);
|
|
||||||
ids.push(item.id);
|
|
||||||
modTimes[item.id] = itemStorageModTimes[libraryKeyHash];
|
|
||||||
}
|
|
||||||
if (ids.length > 0) {
|
|
||||||
Zotero.Sync.Storage.checkForUpdatedFiles(ids, modTimes);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,12 +83,9 @@ Zotero.URI = new function () {
|
||||||
* Get path portion of library URI (e.g., users/6 or groups/1)
|
* Get path portion of library URI (e.g., users/6 or groups/1)
|
||||||
*/
|
*/
|
||||||
this.getLibraryPath = function (libraryID) {
|
this.getLibraryPath = function (libraryID) {
|
||||||
if (libraryID) {
|
libraryID = libraryID ? parseInt(libraryID) : 0;
|
||||||
var libraryType = Zotero.Libraries.getType(libraryID);
|
var libraryType = Zotero.Libraries.getType(libraryID);
|
||||||
}
|
|
||||||
else {
|
|
||||||
libraryType = 'user';
|
|
||||||
}
|
|
||||||
switch (libraryType) {
|
switch (libraryType) {
|
||||||
case 'user':
|
case 'user':
|
||||||
var id = Zotero.userID;
|
var id = Zotero.userID;
|
||||||
|
|
|
@ -153,6 +153,7 @@ var ZoteroPane = new function()
|
||||||
var collectionsTree = document.getElementById('zotero-collections-tree');
|
var collectionsTree = document.getElementById('zotero-collections-tree');
|
||||||
collectionsTree.view = ZoteroPane_Local.collectionsView;
|
collectionsTree.view = ZoteroPane_Local.collectionsView;
|
||||||
collectionsTree.controllers.appendController(new Zotero.CollectionTreeCommandController(collectionsTree));
|
collectionsTree.controllers.appendController(new Zotero.CollectionTreeCommandController(collectionsTree));
|
||||||
|
collectionsTree.addEventListener("mousedown", ZoteroPane_Local.onTreeMouseDown, true);
|
||||||
collectionsTree.addEventListener("click", ZoteroPane_Local.onTreeClick, true);
|
collectionsTree.addEventListener("click", ZoteroPane_Local.onTreeClick, true);
|
||||||
|
|
||||||
var itemsTree = document.getElementById('zotero-items-tree');
|
var itemsTree = document.getElementById('zotero-items-tree');
|
||||||
|
@ -2509,11 +2510,32 @@ var ZoteroPane = new function()
|
||||||
var t = event.originalTarget;
|
var t = event.originalTarget;
|
||||||
var tree = t.parentNode;
|
var tree = t.parentNode;
|
||||||
|
|
||||||
var itemGroup = ZoteroPane_Local.getItemGroup();
|
var row = {}, col = {}, obj = {};
|
||||||
|
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
||||||
|
if (row.value == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var itemGroup = ZoteroPane_Local.collectionsView._getItemAtRow(row.value);
|
||||||
|
|
||||||
|
// Prevent the tree's select event from being called for a click
|
||||||
|
// on a library sync error icon
|
||||||
|
if (tree.id == 'zotero-collections-tree') {
|
||||||
|
if (itemGroup.isLibrary(true)) {
|
||||||
|
if (col.value.id == 'zotero-collections-sync-status-column') {
|
||||||
|
var libraryID = itemGroup.isLibrary() ? 0 : itemGroup.ref.libraryID;
|
||||||
|
var errors = Zotero.Sync.Runner.getErrors(libraryID);
|
||||||
|
if (errors) {
|
||||||
|
event.stopPropagation();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Automatically select all equivalent items when clicking on an item
|
// Automatically select all equivalent items when clicking on an item
|
||||||
// in duplicates view
|
// in duplicates view
|
||||||
if (itemGroup.isDuplicates() && tree.id == 'zotero-items-tree') {
|
else if (tree.id == 'zotero-items-tree' && itemGroup.isDuplicates()) {
|
||||||
// Trigger only on primary-button single clicks with modifiers
|
// Trigger only on primary-button single clicks with modifiers
|
||||||
// (so that items can still be selected and deselected manually)
|
// (so that items can still be selected and deselected manually)
|
||||||
if (!event || event.detail != 1 || event.button != 0 || event.metaKey || event.shiftKey) {
|
if (!event || event.detail != 1 || event.button != 0 || event.metaKey || event.shiftKey) {
|
||||||
|
@ -2558,22 +2580,52 @@ var ZoteroPane = new function()
|
||||||
|
|
||||||
var tree = t.parentNode;
|
var tree = t.parentNode;
|
||||||
|
|
||||||
|
var row = {}, col = {}, obj = {};
|
||||||
|
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
||||||
|
|
||||||
// We care only about primary-button double and triple clicks
|
// We care only about primary-button double and triple clicks
|
||||||
if (!event || (event.detail != 2 && event.detail != 3) || event.button != 0) {
|
if (!event || (event.detail != 2 && event.detail != 3) || event.button != 0) {
|
||||||
|
if (row.value == -1) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var itemGroup = ZoteroPane_Local.collectionsView._getItemAtRow(row.value);
|
||||||
|
|
||||||
|
// Show the error panel when clicking a library-specific
|
||||||
|
// sync error icon
|
||||||
|
if (itemGroup.isLibrary(true)) {
|
||||||
|
if (col.value.id == 'zotero-collections-sync-status-column') {
|
||||||
|
var libraryID = itemGroup.isLibrary() ? 0 : itemGroup.ref.libraryID;
|
||||||
|
var errors = Zotero.Sync.Runner.getErrors(libraryID);
|
||||||
|
if (!errors) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var panel = Zotero.Sync.Runner.updateErrorPanel(window.document, errors);
|
||||||
|
|
||||||
|
var anchor = document.getElementById('zotero-collections-tree-shim');
|
||||||
|
|
||||||
|
var x = {}, y = {}, width = {}, height = {};
|
||||||
|
tree.treeBoxObject.getCoordsForCellItem(row.value, col.value, 'image', x, y, width, height);
|
||||||
|
|
||||||
|
x = x.value + Math.round(width.value / 2);
|
||||||
|
y = y.value + height.value + 3;
|
||||||
|
|
||||||
|
panel.openPopup(anchor, "after_start", x, y, false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// The Mozilla tree binding fires select() in mousedown(),
|
// The Mozilla tree binding fires select() in mousedown(),
|
||||||
// but if when it gets to click() the selection differs from
|
// but if when it gets to click() the selection differs from
|
||||||
// what it expects (say, because multiple items had been
|
// what it expects (say, because multiple items had been
|
||||||
// selected during mousedown()), it fires select() again.
|
// selected during mousedown(), as is the case in duplicates mode),
|
||||||
// We prevent that here.
|
// it fires select() again. We prevent that here.
|
||||||
var itemGroup = ZoteroPane_Local.getItemGroup();
|
else if (itemGroup.isDuplicates() && tree.id == 'zotero-items-tree') {
|
||||||
if (itemGroup.isDuplicates() && tree.id == 'zotero-items-tree') {
|
|
||||||
if (event.metaKey || event.shiftKey) {
|
if (event.metaKey || event.shiftKey) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Allow twisty click to work in duplicates mode
|
|
||||||
var row = {}, col = {}, obj = {};
|
|
||||||
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
|
||||||
if (obj.value == 'twisty') {
|
if (obj.value == 'twisty') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -2597,9 +2649,6 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var row = {}, col = {}, obj = {};
|
|
||||||
tree.treeBoxObject.getCellAt(event.clientX, event.clientY, row, col, obj);
|
|
||||||
|
|
||||||
// obj.value == 'cell'/'text'/'image'
|
// obj.value == 'cell'/'text'/'image'
|
||||||
if (!obj.value) {
|
if (!obj.value) {
|
||||||
return;
|
return;
|
||||||
|
@ -3424,6 +3473,8 @@ var ZoteroPane = new function()
|
||||||
|
|
||||||
|
|
||||||
function viewAttachment(itemIDs, event, noLocateOnMissing, forceExternalViewer) {
|
function viewAttachment(itemIDs, event, noLocateOnMissing, forceExternalViewer) {
|
||||||
|
Components.utils.import("resource://zotero/q.js");
|
||||||
|
|
||||||
// If view isn't editable, don't show Locate button, since the updated
|
// If view isn't editable, don't show Locate button, since the updated
|
||||||
// path couldn't be sent back up
|
// path couldn't be sent back up
|
||||||
if (!this.collectionsView.editable) {
|
if (!this.collectionsView.editable) {
|
||||||
|
@ -3478,38 +3529,39 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
if (item.isImportedAttachment() && Zotero.Sync.Storage.downloadAsNeeded(item.libraryID)) {
|
if (!item.isImportedAttachment() || !Zotero.Sync.Storage.downloadAsNeeded(item.libraryID)) {
|
||||||
let downloadedItem = item;
|
this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing);
|
||||||
var started = Zotero.Sync.Storage.downloadFile(item, {
|
return;
|
||||||
onStart: function (request) {
|
|
||||||
if (!(request instanceof Zotero.Sync.Storage.Request)) {
|
|
||||||
throw new Error("Invalid request object");
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
onProgress: function (progress, progressMax) {
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
onStop: function () {
|
|
||||||
if (!downloadedItem.getFile()) {
|
|
||||||
ZoteroPane_Local.showAttachmentNotFoundDialog(itemID, noLocateOnMissing);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// check if unchanged?
|
|
||||||
// maybe not necessary, since we'll get an error if there's an error
|
|
||||||
|
|
||||||
ZoteroPane_Local.viewAttachment(downloadedItem.id, event, false, forceExternalViewer);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (started) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.showAttachmentNotFoundDialog(itemID, noLocateOnMissing);
|
let downloadedItem = item;
|
||||||
|
Q.fcall(function () {
|
||||||
|
return Zotero.Sync.Storage.downloadFile(
|
||||||
|
downloadedItem,
|
||||||
|
{
|
||||||
|
onProgress: function (progress, progressMax) {}
|
||||||
|
});
|
||||||
|
})
|
||||||
|
.then(function () {
|
||||||
|
if (!downloadedItem.getFile()) {
|
||||||
|
ZoteroPane_Local.showAttachmentNotFoundDialog(downloadedItem.id, noLocateOnMissing);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if unchanged?
|
||||||
|
// maybe not necessary, since we'll get an error if there's an error
|
||||||
|
|
||||||
|
|
||||||
|
Zotero.Notifier.trigger('redraw', 'item', []);
|
||||||
|
|
||||||
|
ZoteroPane_Local.viewAttachment(downloadedItem.id, event, false, forceExternalViewer);
|
||||||
|
})
|
||||||
|
.fail(function (e) {
|
||||||
|
// TODO: show error somewhere else
|
||||||
|
Zotero.debug(e, 1);
|
||||||
|
ZoteroPane_Local.syncAlert(e);
|
||||||
|
})
|
||||||
|
.end();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3744,6 +3796,83 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
this.syncAlert = function (e) {
|
||||||
|
e = Zotero.Sync.Runner.parseSyncError(e);
|
||||||
|
|
||||||
|
var ps = Components.classes["@mozilla.org/embedcomp/prompt-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIPromptService);
|
||||||
|
var buttonFlags = ps.BUTTON_POS_0 * ps.BUTTON_TITLE_OK
|
||||||
|
+ ps.BUTTON_POS_1 * ps.BUTTON_TITLE_IS_STRING;
|
||||||
|
|
||||||
|
// Warning
|
||||||
|
if (e.status == 'warning') {
|
||||||
|
var title = Zotero.getString('general.warning');
|
||||||
|
|
||||||
|
// If secondary button not specified, just use an alert
|
||||||
|
if (e.buttonText) {
|
||||||
|
var buttonText = e.buttonText;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
ps.alert(null, title, e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = ps.confirmEx(
|
||||||
|
null,
|
||||||
|
title,
|
||||||
|
e.message,
|
||||||
|
buttonFlags,
|
||||||
|
"",
|
||||||
|
buttonText,
|
||||||
|
"", null, {}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index == 1) {
|
||||||
|
setTimeout(function () { buttonCallback(); }, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Error
|
||||||
|
else if (e.status == 'error') {
|
||||||
|
var title = Zotero.getString('general.error');
|
||||||
|
|
||||||
|
// If secondary button is explicitly null, just use an alert
|
||||||
|
if (buttonText === null) {
|
||||||
|
ps.alert(null, title, e.message);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (typeof buttonText == 'undefined') {
|
||||||
|
var buttonText = Zotero.getString('errorReport.reportError');
|
||||||
|
var buttonCallback = function () {
|
||||||
|
ZoteroPane.reportErrors();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
var buttonText = e.buttonText;
|
||||||
|
var buttonCallback = e.buttonCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
var index = ps.confirmEx(
|
||||||
|
null,
|
||||||
|
title,
|
||||||
|
e.message,
|
||||||
|
buttonFlags,
|
||||||
|
"",
|
||||||
|
buttonText,
|
||||||
|
"", null, {}
|
||||||
|
);
|
||||||
|
|
||||||
|
if (index == 1) {
|
||||||
|
setTimeout(function () { buttonCallback(); }, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Upgrade
|
||||||
|
else if (e.status == 'upgrade') {
|
||||||
|
ps.alert(null, "", e.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
this.createParentItemsFromSelected = function () {
|
this.createParentItemsFromSelected = function () {
|
||||||
if (!this.canEdit()) {
|
if (!this.canEdit()) {
|
||||||
this.displayCannotEditLibraryMessage();
|
this.displayCannotEditLibraryMessage();
|
||||||
|
|
|
@ -192,32 +192,22 @@
|
||||||
value="0" tooltip="zotero-tb-sync-progress-tooltip">
|
value="0" tooltip="zotero-tb-sync-progress-tooltip">
|
||||||
</progressmeter>
|
</progressmeter>
|
||||||
<tooltip id="zotero-tb-sync-progress-tooltip" noautohide="true">
|
<tooltip id="zotero-tb-sync-progress-tooltip" noautohide="true">
|
||||||
<grid>
|
<hbox id="zotero-tb-sync-progress-tooltip-progress">
|
||||||
<columns>
|
<label value="&zotero.sync.storage.progress;"/>
|
||||||
<column/>
|
<label/>
|
||||||
<column/>
|
</hbox>
|
||||||
</columns>
|
<zoterofilesyncstatus id="zotero-tb-sync-progress-status"/>
|
||||||
<rows>
|
|
||||||
<row>
|
|
||||||
<label value="&zotero.sync.storage.progress;"/>
|
|
||||||
<label id="zotero-tb-sync-progress-tooltip-progress"/>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<label value="&zotero.sync.storage.downloads;"/>
|
|
||||||
<label
|
|
||||||
id="zotero-tb-sync-progress-tooltip-downloads"/>
|
|
||||||
</row>
|
|
||||||
<row>
|
|
||||||
<label value="&zotero.sync.storage.uploads;"/>
|
|
||||||
<label
|
|
||||||
id="zotero-tb-sync-progress-tooltip-uploads"/>
|
|
||||||
</row>
|
|
||||||
</rows>
|
|
||||||
</grid>
|
|
||||||
</tooltip>
|
</tooltip>
|
||||||
</hbox>
|
</hbox>
|
||||||
</hbox>
|
</hbox>
|
||||||
<toolbarbutton id="zotero-tb-sync-warning" hidden="true"/>
|
<toolbarbutton id="zotero-tb-sync-error" hidden="true"/>
|
||||||
|
<!-- We put this here, but it's used for all sync errors -->
|
||||||
|
<panel id="zotero-sync-error-panel" type="arrow">
|
||||||
|
<vbox>
|
||||||
|
<hbox id="zotero-sync-error-panel-content"/>
|
||||||
|
<hbox id="zotero-sync-error-panel-buttons"/>
|
||||||
|
</vbox>
|
||||||
|
</panel>
|
||||||
<toolbarbutton id="zotero-tb-sync" class="zotero-tb-button" tooltip="_child"
|
<toolbarbutton id="zotero-tb-sync" class="zotero-tb-button" tooltip="_child"
|
||||||
oncommand="Zotero.Sync.Server.canAutoResetClient = true; Zotero.Sync.Server.manualSyncRequired = false; Zotero.Sync.Runner.sync()">
|
oncommand="Zotero.Sync.Server.canAutoResetClient = true; Zotero.Sync.Server.manualSyncRequired = false; Zotero.Sync.Runner.sync()">
|
||||||
<tooltip
|
<tooltip
|
||||||
|
@ -292,6 +282,10 @@
|
||||||
|
|
||||||
<hbox id="zotero-trees" flex="1">
|
<hbox id="zotero-trees" flex="1">
|
||||||
<vbox id="zotero-collections-pane" zotero-persist="width">
|
<vbox id="zotero-collections-pane" zotero-persist="width">
|
||||||
|
<!-- This is used for positioning the sync error icon panel
|
||||||
|
under specific tree cells, which don't exist as
|
||||||
|
elements on their own -->
|
||||||
|
<box id="zotero-collections-tree-shim"/>
|
||||||
<!-- This extra vbox prevents the toolbar from getting compressed when resizing
|
<!-- This extra vbox prevents the toolbar from getting compressed when resizing
|
||||||
the tag selector to max height -->
|
the tag selector to max height -->
|
||||||
<tree id="zotero-collections-tree" hidecolumnpicker="true" context="zotero-collectionmenu"
|
<tree id="zotero-collections-tree" hidecolumnpicker="true" context="zotero-collectionmenu"
|
||||||
|
@ -309,6 +303,9 @@
|
||||||
flex="1"
|
flex="1"
|
||||||
primary="true"
|
primary="true"
|
||||||
hideheader="true"/>
|
hideheader="true"/>
|
||||||
|
<treecol
|
||||||
|
id="zotero-collections-sync-status-column"
|
||||||
|
hideheader="true"/>
|
||||||
</treecols>
|
</treecols>
|
||||||
<treechildren/>
|
<treechildren/>
|
||||||
</tree>
|
</tree>
|
||||||
|
|
|
@ -683,9 +683,12 @@ sync.status.uploadingData = Uploading data to sync server
|
||||||
sync.status.uploadAccepted = Upload accepted \u2014 waiting for sync server
|
sync.status.uploadAccepted = Upload accepted \u2014 waiting for sync server
|
||||||
sync.status.syncingFiles = Syncing files
|
sync.status.syncingFiles = Syncing files
|
||||||
|
|
||||||
|
sync.storage.mbRemaining = %SMB remaining
|
||||||
sync.storage.kbRemaining = %SKB remaining
|
sync.storage.kbRemaining = %SKB remaining
|
||||||
sync.storage.filesRemaining = %1$S/%2$S files
|
sync.storage.filesRemaining = %1$S/%2$S files
|
||||||
sync.storage.none = None
|
sync.storage.none = None
|
||||||
|
sync.storage.downloads = Downloads:
|
||||||
|
sync.storage.uploads = Uploads:
|
||||||
sync.storage.localFile = Local File
|
sync.storage.localFile = Local File
|
||||||
sync.storage.remoteFile = Remote File
|
sync.storage.remoteFile = Remote File
|
||||||
sync.storage.savedFile = Saved File
|
sync.storage.savedFile = Saved File
|
||||||
|
|
|
@ -20,11 +20,19 @@
|
||||||
min-height: 5.2em;
|
min-height: 5.2em;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zotero-collections-tree treechildren::-moz-tree-image
|
#zotero-collections-tree treechildren::-moz-tree-image(primary)
|
||||||
{
|
{
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#zotero-collections-tree #zotero-collections-sync-status-column {
|
||||||
|
width: 35px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zotero-collections-tree[hidevscroll] #zotero-collections-sync-status-column {
|
||||||
|
width: 21px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Set by setHighlightedRows() and getRowProperties() in collectionTreeView.js) */
|
/* Set by setHighlightedRows() and getRowProperties() in collectionTreeView.js) */
|
||||||
#zotero-collections-tree treechildren::-moz-tree-row(highlighted)
|
#zotero-collections-tree treechildren::-moz-tree-row(highlighted)
|
||||||
{
|
{
|
||||||
|
@ -54,6 +62,145 @@
|
||||||
margin-right: 5px;
|
margin-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie)
|
||||||
|
{
|
||||||
|
margin: 1px 0 0;
|
||||||
|
list-style-image: url(chrome://zotero/skin/pie.png);
|
||||||
|
height: 16px;
|
||||||
|
-moz-image-region: rect(0px, 32px, 32px, 0px);
|
||||||
|
}
|
||||||
|
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie1) { -moz-image-region: rect(0px, 32px, 32px, 0x); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie2) { -moz-image-region: rect(0px, 64px, 32px, 32px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie3) { -moz-image-region: rect(0px, 96px, 32px, 64px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie4) { -moz-image-region: rect(0px, 128px, 32px, 96px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie5) { -moz-image-region: rect(0px, 160px, 32px, 128px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie6) { -moz-image-region: rect(0px, 192px, 32px, 160px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie7) { -moz-image-region: rect(0px, 224px, 32px, 192px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie8) { -moz-image-region: rect(0px, 256px, 32px, 224px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie9) { -moz-image-region: rect(0px, 288px, 32px, 256px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie10) { -moz-image-region: rect(0px, 320px, 32px, 288px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie11) { -moz-image-region: rect(0px, 352px, 32px, 320px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie12) { -moz-image-region: rect(0px, 384px, 32px, 352px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie13) { -moz-image-region: rect(0px, 416px, 32px, 384px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie14) { -moz-image-region: rect(0px, 448px, 32px, 416px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie15) { -moz-image-region: rect(0px, 480px, 32px, 448px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie16) { -moz-image-region: rect(0px, 512px, 32px, 480px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie17) { -moz-image-region: rect(0px, 544px, 32px, 512px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie18) { -moz-image-region: rect(0px, 576px, 32px, 544px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie19) { -moz-image-region: rect(0px, 608px, 32px, 576px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie20) { -moz-image-region: rect(0px, 640px, 32px, 608px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie21) { -moz-image-region: rect(0px, 672px, 32px, 640px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie22) { -moz-image-region: rect(0px, 704px, 32px, 672px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie23) { -moz-image-region: rect(0px, 736px, 32px, 704px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie24) { -moz-image-region: rect(0px, 768px, 32px, 736px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie25) { -moz-image-region: rect(0px, 800px, 32px, 768px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie26) { -moz-image-region: rect(0px, 832px, 32px, 800px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie27) { -moz-image-region: rect(0px, 864px, 32px, 832px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie28) { -moz-image-region: rect(0px, 896px, 32px, 864px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie29) { -moz-image-region: rect(0px, 928px, 32px, 896px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie30) { -moz-image-region: rect(0px, 960px, 32px, 928px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie31) { -moz-image-region: rect(0px, 992px, 32px, 960px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie32) { -moz-image-region: rect(0px, 1024px, 32px, 992px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie33) { -moz-image-region: rect(0px, 1056px, 32px, 1024px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie34) { -moz-image-region: rect(0px, 1088px, 32px, 1056px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie35) { -moz-image-region: rect(0px, 1120px, 32px, 1088px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie36) { -moz-image-region: rect(0px, 1152px, 32px, 1120px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie37) { -moz-image-region: rect(0px, 1184px, 32px, 1152px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie38) { -moz-image-region: rect(0px, 1216px, 32px, 1184px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie39) { -moz-image-region: rect(0px, 1248px, 32px, 1216px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie40) { -moz-image-region: rect(0px, 1280px, 32px, 1248px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie41) { -moz-image-region: rect(0px, 1312px, 32px, 1280px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie42) { -moz-image-region: rect(0px, 1344px, 32px, 1312px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie43) { -moz-image-region: rect(0px, 1376px, 32px, 1344px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie44) { -moz-image-region: rect(0px, 1408px, 32px, 1376px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie45) { -moz-image-region: rect(0px, 1440px, 32px, 1408px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie46) { -moz-image-region: rect(0px, 1472px, 32px, 1440px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie47) { -moz-image-region: rect(0px, 1504px, 32px, 1472px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie48) { -moz-image-region: rect(0px, 1536px, 32px, 1504px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie49) { -moz-image-region: rect(0px, 1568px, 32px, 1536px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie50) { -moz-image-region: rect(0px, 1600px, 32px, 1568px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie51) { -moz-image-region: rect(0px, 1632px, 32px, 1600px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie52) { -moz-image-region: rect(0px, 1664px, 32px, 1632px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie53) { -moz-image-region: rect(0px, 1696px, 32px, 1664px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie54) { -moz-image-region: rect(0px, 1728px, 32px, 1696px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie55) { -moz-image-region: rect(0px, 1760px, 32px, 1728px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie56) { -moz-image-region: rect(0px, 1792px, 32px, 1760px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie57) { -moz-image-region: rect(0px, 1824px, 32px, 1792px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie58) { -moz-image-region: rect(0px, 1856px, 32px, 1824px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie59) { -moz-image-region: rect(0px, 1888px, 32px, 1856px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie60) { -moz-image-region: rect(0px, 1920px, 32px, 1888px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie61) { -moz-image-region: rect(0px, 1952px, 32px, 1920px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie62) { -moz-image-region: rect(0px, 1984px, 32px, 1952px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie63) { -moz-image-region: rect(0px, 2016px, 32px, 1984px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(hasAttachment, pie64) { -moz-image-region: rect(0px, 2048px, 32px, 2016px); }
|
||||||
|
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie1) { -moz-image-region: rect(32px, 32px, 64px, 0px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie2) { -moz-image-region: rect(32px, 64px, 64px, 32px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie3) { -moz-image-region: rect(32px, 96px, 64px, 64px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie4) { -moz-image-region: rect(32px, 128px, 64px, 96px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie5) { -moz-image-region: rect(32px, 160px, 64px, 128px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie6) { -moz-image-region: rect(32px, 192px, 64px, 160px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie7) { -moz-image-region: rect(32px, 224px, 64px, 192px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie8) { -moz-image-region: rect(32px, 256px, 64px, 224px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie9) { -moz-image-region: rect(32px, 288px, 64px, 256px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie10) { -moz-image-region: rect(32px, 320px, 64px, 288px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie11) { -moz-image-region: rect(32px, 352px, 64px, 320px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie12) { -moz-image-region: rect(32px, 384px, 64px, 352px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie13) { -moz-image-region: rect(32px, 416px, 64px, 384px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie14) { -moz-image-region: rect(32px, 448px, 64px, 416px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie15) { -moz-image-region: rect(32px, 480px, 64px, 448px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie16) { -moz-image-region: rect(32px, 512px, 64px, 480px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie17) { -moz-image-region: rect(32px, 544px, 64px, 512px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie18) { -moz-image-region: rect(32px, 576px, 64px, 544px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie19) { -moz-image-region: rect(32px, 608px, 64px, 576px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie20) { -moz-image-region: rect(32px, 640px, 64px, 608px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie21) { -moz-image-region: rect(32px, 672px, 64px, 640px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie22) { -moz-image-region: rect(32px, 704px, 64px, 672px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie23) { -moz-image-region: rect(32px, 736px, 64px, 704px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie24) { -moz-image-region: rect(32px, 768px, 64px, 736px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie25) { -moz-image-region: rect(32px, 800px, 64px, 768px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie26) { -moz-image-region: rect(32px, 832px, 64px, 800px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie27) { -moz-image-region: rect(32px, 864px, 64px, 832px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie28) { -moz-image-region: rect(32px, 896px, 64px, 864px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie29) { -moz-image-region: rect(32px, 928px, 64px, 896px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie30) { -moz-image-region: rect(32px, 960px, 64px, 928px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie31) { -moz-image-region: rect(32px, 992px, 64px, 960px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie32) { -moz-image-region: rect(32px, 1024px, 64px, 992px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie33) { -moz-image-region: rect(32px, 1056px, 64px, 1024px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie34) { -moz-image-region: rect(32px, 1088px, 64px, 1056px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie35) { -moz-image-region: rect(32px, 1120px, 64px, 1088px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie36) { -moz-image-region: rect(32px, 1152px, 64px, 1120px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie37) { -moz-image-region: rect(32px, 1184px, 64px, 1152px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie38) { -moz-image-region: rect(32px, 1216px, 64px, 1184px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie39) { -moz-image-region: rect(32px, 1248px, 64px, 1216px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie40) { -moz-image-region: rect(32px, 1280px, 64px, 1248px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie41) { -moz-image-region: rect(32px, 1312px, 64px, 1280px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie42) { -moz-image-region: rect(32px, 1344px, 64px, 1312px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie43) { -moz-image-region: rect(32px, 1376px, 64px, 1344px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie44) { -moz-image-region: rect(32px, 1408px, 64px, 1376px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie45) { -moz-image-region: rect(32px, 1440px, 64px, 1408px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie46) { -moz-image-region: rect(32px, 1472px, 64px, 1440px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie47) { -moz-image-region: rect(32px, 1504px, 64px, 1472px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie48) { -moz-image-region: rect(32px, 1536px, 64px, 1504px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie49) { -moz-image-region: rect(32px, 1568px, 64px, 1536px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie50) { -moz-image-region: rect(32px, 1600px, 64px, 1568px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie51) { -moz-image-region: rect(32px, 1632px, 64px, 1600px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie52) { -moz-image-region: rect(32px, 1664px, 64px, 1632px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie53) { -moz-image-region: rect(32px, 1696px, 64px, 1664px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie54) { -moz-image-region: rect(32px, 1728px, 64px, 1696px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie55) { -moz-image-region: rect(32px, 1760px, 64px, 1728px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie56) { -moz-image-region: rect(32px, 1792px, 64px, 1760px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie57) { -moz-image-region: rect(32px, 1824px, 64px, 1792px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie58) { -moz-image-region: rect(32px, 1856px, 64px, 1824px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie59) { -moz-image-region: rect(32px, 1888px, 64px, 1856px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie60) { -moz-image-region: rect(32px, 1920px, 64px, 1888px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie61) { -moz-image-region: rect(32px, 1952px, 64px, 1920px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie62) { -moz-image-region: rect(32px, 1984px, 64px, 1952px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie63) { -moz-image-region: rect(32px, 2016px, 64px, 1984px); }
|
||||||
|
#zotero-items-tree treechildren::-moz-tree-image(selected, hasAttachment, pie64) { -moz-image-region: rect(32px, 2048px, 64px, 2016px); }
|
||||||
|
|
||||||
|
|
||||||
/* Set tag colors */
|
/* Set tag colors */
|
||||||
#zotero-items-tree treechildren::-moz-tree-cell-text(colorFFFFFF) { color:#FFFFFF }
|
#zotero-items-tree treechildren::-moz-tree-cell-text(colorFFFFFF) { color:#FFFFFF }
|
||||||
#zotero-items-tree treechildren::-moz-tree-cell-text(colorFFCCCC) { color:#FFCCCC }
|
#zotero-items-tree treechildren::-moz-tree-cell-text(colorFFCCCC) { color:#FFCCCC }
|
||||||
|
@ -380,28 +527,58 @@
|
||||||
margin-left: 0;
|
margin-left: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zotero-tb-sync-progress-tooltip row label:first-child
|
#zotero-tb-sync-progress-tooltip-progress {
|
||||||
{
|
margin-bottom: 5px;
|
||||||
text-align: right;
|
|
||||||
font-weight: bold;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#zotero-tb-sync-warning, #zotero-tb-sync-warning[mode=warning]
|
/* Library names */
|
||||||
|
#zotero-tb-sync-progress-tooltip rows > label
|
||||||
|
{
|
||||||
|
font-weight: bold;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Queue names */
|
||||||
|
#zotero-tb-sync-progress-tooltip row:not(.library-name) label:first-child
|
||||||
|
{
|
||||||
|
text-align: right;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sync error icon */
|
||||||
|
#zotero-tb-sync-error, #zotero-tb-sync-error[mode=warning]
|
||||||
{
|
{
|
||||||
list-style-image: url(chrome://zotero/skin/error.png);
|
list-style-image: url(chrome://zotero/skin/error.png);
|
||||||
margin-right: -5px;
|
margin-right: -5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
#zotero-tb-sync-warning[mode=error]
|
#zotero-tb-sync-error[mode=error]
|
||||||
{
|
{
|
||||||
list-style-image: url(chrome://zotero/skin/exclamation.png);
|
list-style-image: url(chrome://zotero/skin/exclamation.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
#zotero-tb-sync-warning[mode=upgrade]
|
#zotero-tb-sync-error[mode=upgrade]
|
||||||
{
|
{
|
||||||
list-style-image: url(chrome://zotero/skin/bell_error.png);
|
list-style-image: url(chrome://zotero/skin/bell_error.png);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#zotero-tb-sync-error {
|
||||||
|
/*border: 1px orange dashed;*/
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Sync error panel */
|
||||||
|
#zotero-sync-error-panel {
|
||||||
|
margin-right: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zotero-sync-error-panel description {
|
||||||
|
width: 350px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
#zotero-sync-error-panel-buttons {
|
||||||
|
-moz-box-pack: end;
|
||||||
|
}
|
||||||
|
|
||||||
#zotero-tb-sync {
|
#zotero-tb-sync {
|
||||||
list-style-image: url(chrome://zotero/skin/arrow_rotate_static.png);
|
list-style-image: url(chrome://zotero/skin/arrow_rotate_static.png);
|
||||||
margin-left: -6px;
|
margin-left: -6px;
|
||||||
|
|
BIN
chrome/skin/default/zotero/pie.png
Normal file
BIN
chrome/skin/default/zotero/pie.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 18 KiB |
|
@ -156,6 +156,11 @@ zoteroguidancepanel
|
||||||
-moz-binding: url('chrome://zotero/content/bindings/columnpicker.xml#extended-columnpicker');
|
-moz-binding: url('chrome://zotero/content/bindings/columnpicker.xml#extended-columnpicker');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
zoterofilesyncstatus {
|
||||||
|
-moz-binding: url('chrome://zotero/content/bindings/filesyncstatus.xml#file-sync-status');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
label.zotero-text-link {
|
label.zotero-text-link {
|
||||||
-moz-binding: url('chrome://zotero/content/bindings/text-link.xml#text-link');
|
-moz-binding: url('chrome://zotero/content/bindings/text-link.xml#text-link');
|
||||||
-moz-user-focus: normal;
|
-moz-user-focus: normal;
|
||||||
|
|
|
@ -95,7 +95,7 @@ const xpcomFilesLocal = [
|
||||||
'sync',
|
'sync',
|
||||||
'storage',
|
'storage',
|
||||||
'storage/streamListener',
|
'storage/streamListener',
|
||||||
'storage/eventManager',
|
'storage/eventLog',
|
||||||
'storage/queueManager',
|
'storage/queueManager',
|
||||||
'storage/queue',
|
'storage/queue',
|
||||||
'storage/request',
|
'storage/request',
|
||||||
|
|
Loading…
Reference in New Issue
Block a user