Merge branch '4.0'
Conflicts: chrome/content/zotero/xpcom/zotero.js
This commit is contained in:
commit
1117332177
|
@ -54,6 +54,7 @@ overlay chrome://zotero/content/preferences/preferences.xul chrome://zotero/cont
|
|||
overlay chrome://zotero/content/preferences/preferences.xul#cite chrome://zotero/content/preferences/preferences_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
|
||||
overlay chrome://zotero/content/preferences/preferences_general.xul chrome://zotero/content/preferences/preferences_general_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
|
||||
overlay chrome://zotero/content/preferences/preferences_export.xul chrome://zotero/content/preferences/preferences_export_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
|
||||
overlay chrome://zotero/content/preferences/preferences_keys.xul chrome://zotero/content/preferences/preferences_keys_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
|
||||
overlay chrome://zotero/content/preferences/preferences_advanced.xul chrome://zotero/content/preferences/preferences_advanced_firefox.xul application={ec8030f7-c20a-464f-9b0e-13a3a9e97384}
|
||||
overlay chrome://zotero/content/preferences/preferences_advanced.xul chrome://zotero/content/preferences/preferences_advanced_standalone.xul application=zotero@chnm.gmu.edu
|
||||
|
||||
|
|
|
@ -538,7 +538,7 @@
|
|||
<label id="index-status-label"/>
|
||||
<hbox>
|
||||
<label id="index-status"/>
|
||||
<toolbarbutton id="reindex" oncommand="this.hidden = true; setTimeout(function () { ZoteroPane_Local.reindexItem(); }, 50)"/>
|
||||
<image id="reindex" onclick="this.hidden = true; setTimeout(function () { ZoteroPane_Local.reindexItem(); }, 50)"/>
|
||||
</hbox>
|
||||
</row>
|
||||
</rows>
|
||||
|
|
|
@ -94,6 +94,10 @@
|
|||
<key id="key_openZotero"
|
||||
key="Z"
|
||||
oncommand="ZoteroOverlay.toggleDisplay();"
|
||||
modifiers="accel alt" />
|
||||
modifiers="accel shift" />
|
||||
<key id="key_saveToZotero"
|
||||
key="S"
|
||||
oncommand="Zotero_Browser.scrapeThisPage();"
|
||||
modifiers="accel shift" />
|
||||
</keyset>
|
||||
</overlay>
|
||||
|
|
|
@ -27,10 +27,10 @@
|
|||
|
||||
Zotero_Preferences.Keys = {
|
||||
init: function () {
|
||||
// Display the appropriate modifier keys for the platform
|
||||
var rows = document.getElementById('zotero-prefpane-keys').getElementsByTagName('row');
|
||||
for (var i=0; i<rows.length; i++) {
|
||||
rows[i].firstChild.nextSibling.value = Zotero.isMac ? 'Cmd+Shift+' : 'Ctrl+Alt+';
|
||||
// Display the appropriate modifier keys for the platform
|
||||
rows[i].firstChild.nextSibling.value = Zotero.isMac ? 'Cmd+Shift+' : 'Ctrl+Shift+';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -31,50 +31,27 @@
|
|||
helpTopic="shortcut_keys">
|
||||
<preferences>
|
||||
<preference id="pref-keys-openZotero" name="extensions.zotero.keys.openZotero" type="string"/>
|
||||
<preference id="pref-keys-saveToZotero" name="extensions.zotero.keys.saveToZotero" type="string"/>
|
||||
<preference id="pref-keys-toggleFullscreen" name="extensions.zotero.keys.toggleFullscreen" type="string"/>
|
||||
<preference id="pref-keys-library" name="extensions.zotero.keys.library" type="string"/>
|
||||
<preference id="pref-keys-quicksearch" name="extensions.zotero.keys.quicksearch" type="string"/>
|
||||
<preference id="pref-keys-toggleTagSelector" name="extensions.zotero.keys.toggleTagSelector" type="string"/>
|
||||
<preference id="pref-keys-newItem" name="extensions.zotero.keys.newItem" type="string"/>
|
||||
<preference id="pref-keys-newNote" name="extensions.zotero.keys.newNote" type="string"/>
|
||||
<preference id="pref-keys-toggleTagSelector" name="extensions.zotero.keys.toggleTagSelector" type="string"/>
|
||||
<preference id="pref-keys-importFromClipboard" name="extensions.zotero.keys.importFromClipboard" type="string"/>
|
||||
<preference id="pref-keys-copySelectedItemCitationsToClipboard" name="extensions.zotero.keys.copySelectedItemCitationsToClipboard" type="string"/>
|
||||
<preference id="pref-keys-copySelectedItemsToClipboard" name="extensions.zotero.keys.copySelectedItemsToClipboard" type="string"/>
|
||||
<preference id="pref-keys-importFromClipboard" name="extensions.zotero.keys.importFromClipboard" type="string"/>
|
||||
</preferences>
|
||||
|
||||
<grid>
|
||||
<grid id="zotero-keys-grid">
|
||||
<columns>
|
||||
<column flex="1"/>
|
||||
<column/>
|
||||
<column/>
|
||||
</columns>
|
||||
|
||||
<rows>
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.openZotero;" control="key-textbox-openZotero"/>
|
||||
<label/>
|
||||
<textbox id="textbox-openZotero" maxlength="1" size="1" preference="pref-keys-openZotero"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.toggleFullscreen;" control="textbox-toggleFullscreen"/>
|
||||
<label/>
|
||||
<textbox id="textbox-toggleFullscreen" maxlength="1" size="1" preference="pref-keys-toggleFullscreen"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.focusLibrariesPane;" control="textbox-library"/>
|
||||
<label/>
|
||||
<textbox id="textbox-library" maxlength="1" size="1" preference="pref-keys-library"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.quicksearch;" control="textbox-quicksearch"/>
|
||||
<label/>
|
||||
<textbox id="textbox-quicksearch" maxlength="1" size="1" preference="pref-keys-quicksearch"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<rows id="zotero-keys-rows">
|
||||
<row id="zotero-keys-new-item">
|
||||
<label value="&zotero.preferences.keys.newItem;" control="textbox-newItem"/>
|
||||
<label/>
|
||||
<textbox id="textbox-newItem" maxlength="1" size="1" preference="pref-keys-newItem"/>
|
||||
|
@ -87,9 +64,21 @@
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.toggleTagSelector;" control="textbox-toggleTagSelector"/>
|
||||
<label value="&zotero.preferences.keys.importFromClipboard;" control="textbox-importFromClipboard"/>
|
||||
<label/>
|
||||
<textbox id="textbox-toggleTagSelector" maxlength="1" size="1" preference="pref-keys-toggleTagSelector"/>
|
||||
<textbox id="textbox-importFromClipboard" maxlength="1" size="1" preference="pref-keys-importFromClipboard"/>
|
||||
</row>
|
||||
|
||||
<row id="zotero-keys-focus-libraries-pane">
|
||||
<label value="&zotero.preferences.keys.focusLibrariesPane;" control="textbox-library"/>
|
||||
<label/>
|
||||
<textbox id="textbox-library" maxlength="1" size="1" preference="pref-keys-library"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.quicksearch;" control="textbox-quicksearch"/>
|
||||
<label/>
|
||||
<textbox id="textbox-quicksearch" maxlength="1" size="1" preference="pref-keys-quicksearch"/>
|
||||
</row>
|
||||
|
||||
<row>
|
||||
|
@ -109,9 +98,9 @@
|
|||
</row>
|
||||
|
||||
<row>
|
||||
<label value="&zotero.preferences.keys.importFromClipboard;" control="textbox-importFromClipboard"/>
|
||||
<label value="&zotero.preferences.keys.toggleTagSelector;" control="textbox-toggleTagSelector"/>
|
||||
<label/>
|
||||
<textbox id="textbox-importFromClipboard" maxlength="1" size="1" preference="pref-keys-importFromClipboard"/>
|
||||
<textbox id="textbox-toggleTagSelector" maxlength="1" size="1" preference="pref-keys-toggleTagSelector"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
|
|
|
@ -0,0 +1,52 @@
|
|||
<?xml version="1.0"?>
|
||||
<!--
|
||||
***** BEGIN LICENSE BLOCK *****
|
||||
|
||||
Copyright © 2006–2013 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 *****
|
||||
-->
|
||||
<!DOCTYPE prefwindow SYSTEM "chrome://zotero/locale/preferences.dtd">
|
||||
|
||||
<overlay xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<prefpane id="zotero-prefpane-keys">
|
||||
<grid id="zotero-keys-grid">
|
||||
<rows id="zotero-keys-rows">
|
||||
<row position="1">
|
||||
<label value="&zotero.preferences.keys.openZotero;" control="key-textbox-openZotero"/>
|
||||
<label/>
|
||||
<textbox id="textbox-openZotero" maxlength="1" size="1" preference="pref-keys-openZotero"/>
|
||||
</row>
|
||||
|
||||
<row position="2">
|
||||
<label value="&zotero.preferences.keys.toggleFullscreen;" control="textbox-toggleFullscreen"/>
|
||||
<label/>
|
||||
<textbox id="textbox-toggleFullscreen" maxlength="1" size="1" preference="pref-keys-toggleFullscreen"/>
|
||||
</row>
|
||||
|
||||
<row insertbefore="zotero-keys-new-item">
|
||||
<label value="&zotero.preferences.keys.saveToZotero;" control="key-textbox-saveToZotero"/>
|
||||
<label/>
|
||||
<textbox id="textbox-saveToZotero" maxlength="1" size="1" preference="pref-keys-saveToZotero"/>
|
||||
</row>
|
||||
</rows>
|
||||
</grid>
|
||||
</prefpane>
|
||||
</overlay>
|
|
@ -38,6 +38,7 @@ const ZoteroStandalone = new function() {
|
|||
window.close();
|
||||
return;
|
||||
}
|
||||
_checkRoot();
|
||||
ZoteroPane.init();
|
||||
ZoteroPane.makeVisible();
|
||||
|
||||
|
@ -146,6 +147,32 @@ const ZoteroStandalone = new function() {
|
|||
this.onUnload = function() {
|
||||
ZoteroPane.destroy();
|
||||
}
|
||||
|
||||
/**
|
||||
* Warn if Zotero Standalone is running as root and clobber the cache directory
|
||||
*/
|
||||
function _checkRoot() {
|
||||
if(!Zotero.isWin) {
|
||||
var env = Components.classes["@mozilla.org/process/environment;1"].
|
||||
getService(Components.interfaces.nsIEnvironment);
|
||||
var user = env.get("USER") || env.get("USERNAME");
|
||||
if(user === "root") {
|
||||
// Zap cache files
|
||||
try {
|
||||
Services.dirsvc.get("ProfLD", Components.interfaces.nsIFile).remove(true);
|
||||
} catch(e) {}
|
||||
// Warn user never to do this again
|
||||
if(Services.prompt.confirmEx(null, "", Zotero.getString("standalone.rootWarning"),
|
||||
Services.prompt.BUTTON_POS_0*Services.prompt.BUTTON_TITLE_IS_STRING |
|
||||
Services.prompt.BUTTON_POS_1*Services.prompt.BUTTON_TITLE_IS_STRING,
|
||||
Zotero.getString("standalone.rootWarning.exit"),
|
||||
Zotero.getString("standalone.rootWarning.continue"),
|
||||
null, null, {}) == 0) {
|
||||
goQuitApplication();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Taken from browser.js **/
|
||||
|
|
10
chrome/content/zotero/standalone/updatesOverlay.xul
Normal file
10
chrome/content/zotero/standalone/updatesOverlay.xul
Normal file
|
@ -0,0 +1,10 @@
|
|||
<?xml version="1.0"?>
|
||||
<overlay id="zotero-update"
|
||||
xmlns="http://www.mozilla.org/keymaster/gatekeeper/there.is.only.xul">
|
||||
<wizard id="updates">
|
||||
<script type="application/javascript" src="chrome://zotero/content/include.js"/>
|
||||
<script type="application/javascript">
|
||||
document.getElementById("manualUpdateDesc").textContent = Zotero.getString("standalone.updateMessage");
|
||||
</script>
|
||||
</wizard>
|
||||
</overlay>
|
|
@ -11,7 +11,7 @@
|
|||
style="padding:2em">
|
||||
|
||||
<script src="../include.js"/>
|
||||
<script src="test.js"/>
|
||||
<script type="application/javascript;version=1.8" src="test.js"/>
|
||||
|
||||
<hbox><label>This is a test page.</label></hbox>
|
||||
</window>
|
||||
|
|
|
@ -57,7 +57,7 @@ if (!Array.indexOf) {
|
|||
};
|
||||
}
|
||||
var CSL = {
|
||||
PROCESSOR_VERSION: "1.0.470",
|
||||
PROCESSOR_VERSION: "1.0.471",
|
||||
CONDITION_LEVEL_TOP: 1,
|
||||
CONDITION_LEVEL_BOTTOM: 2,
|
||||
PLAIN_HYPHEN_REGEX: /(?:[^\\]-|\u2013)/,
|
||||
|
@ -2860,7 +2860,7 @@ CSL.Output.Queue.prototype.string = function (state, myblobs, blob) {
|
|||
if (blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) {
|
||||
span_split = ret.length;
|
||||
}
|
||||
var blobs_start = state.output.renderBlobs(ret.slice(0, span_split), blob_delimiter, true);
|
||||
var blobs_start = state.output.renderBlobs(ret.slice(0, span_split), blob_delimiter, true, blob);
|
||||
if (blobs_start && blob && (blob.decorations.length || blob.strings.suffix || blob.strings.prefix)) {
|
||||
if (!state.tmp.suppress_decorations) {
|
||||
for (i = 0, ilen = blob.decorations.length; i < ilen; i += 1) {
|
||||
|
@ -2925,7 +2925,7 @@ CSL.Output.Queue.prototype.clearlevel = function () {
|
|||
blob.blobs.pop();
|
||||
}
|
||||
};
|
||||
CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite) {
|
||||
CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite, parent) {
|
||||
var state, ret, ret_last_char, use_delim, i, blob, pos, len, ppos, llen, pppos, lllen, res, str, params, txt_esc;
|
||||
txt_esc = CSL.getSafeEscape(this.state);
|
||||
if (!delim) {
|
||||
|
@ -2936,6 +2936,11 @@ CSL.Output.Queue.prototype.renderBlobs = function (blobs, delim, in_cite) {
|
|||
ret_last_char = [];
|
||||
use_delim = "";
|
||||
len = blobs.length;
|
||||
if (this.state.tmp.area === "citation" && !this.state.tmp.just_looking && len === 1 && typeof blobs[0] === "object" && parent) {
|
||||
blobs[0].strings.prefix = parent.strings.prefix + blobs[0].strings.prefix;
|
||||
blobs[0].strings.suffix = blobs[0].strings.suffix + parent.strings.suffix;
|
||||
return blobs[0];
|
||||
}
|
||||
var start = true;
|
||||
for (pos = 0; pos < len; pos += 1) {
|
||||
if (blobs[pos].checkNext) {
|
||||
|
|
|
@ -1725,7 +1725,16 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient)
|
|||
|
||||
try {
|
||||
Zotero.DB.beginTransaction();
|
||||
if (dragData.dropEffect == 'link') {
|
||||
var itemID = Zotero.Attachments.linkFromFile(file);
|
||||
}
|
||||
else {
|
||||
if (dragData.dropEffect != 'copy') {
|
||||
Components.utils.reportError("Invalid dropEffect '" + dragData.dropEffect + "' dropping file");
|
||||
}
|
||||
var itemID = Zotero.Attachments.importFromFile(file, false, targetLibraryID);
|
||||
}
|
||||
|
||||
if (parentCollectionID) {
|
||||
var col = Zotero.Collections.get(parentCollectionID);
|
||||
if (col) {
|
||||
|
@ -1751,17 +1760,38 @@ Zotero.CollectionTreeView.prototype.drop = function(row, orient)
|
|||
* Called by HTML 5 Drag and Drop when dragging over the tree
|
||||
*/
|
||||
Zotero.CollectionTreeView.prototype.onDragEnter = function (event) {
|
||||
//Zotero.debug("Storing current drag data");
|
||||
Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Called by HTML 5 Drag and Drop when dragging over the tree
|
||||
*/
|
||||
Zotero.CollectionTreeView.prototype.onDragOver = function (event, dropdata, session) {
|
||||
Zotero.CollectionTreeView.prototype.onDragOver = function (event) {
|
||||
Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
|
||||
if (event.dataTransfer.types.contains("application/x-moz-file")) {
|
||||
// As of Aug. 2013 nightlies:
|
||||
//
|
||||
// - Setting the dropEffect only works on Linux and OS X.
|
||||
//
|
||||
// - Modifier keys don't show up in the drag event on OS X until the
|
||||
// drop, so since we can't show a correct effect, we leave it at
|
||||
// the default 'move', the least misleading option.
|
||||
//
|
||||
// - The cursor effect gets set by the system on Windows 7 and can't
|
||||
// be overridden.
|
||||
if (!Zotero.isMac) {
|
||||
if (event.ctrlKey && event.shiftKey) {
|
||||
event.dataTransfer.dropEffect = "link";
|
||||
}
|
||||
else {
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Show copy symbol when dragging an item over a collection
|
||||
if (event.dataTransfer.getData("zotero/item")) {
|
||||
else if (event.dataTransfer.getData("zotero/item")) {
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
return false;
|
||||
|
@ -1771,7 +1801,8 @@ Zotero.CollectionTreeView.prototype.onDragOver = function (event, dropdata, sess
|
|||
/*
|
||||
* Called by HTML 5 Drag and Drop when dropping onto the tree
|
||||
*/
|
||||
Zotero.CollectionTreeView.prototype.onDrop = function (event, dropdata, session) {
|
||||
Zotero.CollectionTreeView.prototype.onDrop = function (event) {
|
||||
Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -132,6 +132,7 @@ Zotero.Item.prototype.__defineSetter__('relatedItems', function (arr) { this._se
|
|||
Zotero.Item.prototype.__defineGetter__('relatedItemsReverse', function () { var ids = this._getRelatedItemsReverse(); return ids; });
|
||||
Zotero.Item.prototype.__defineGetter__('relatedItemsBidirectional', function () { var ids = this._getRelatedItemsBidirectional(); return ids; });
|
||||
|
||||
Zotero.Item.prototype.__defineGetter__('libraryKey', function () this.libraryIDInt + "/" + this.key);
|
||||
|
||||
Zotero.Item.prototype.getID = function() {
|
||||
Zotero.debug('Item.getID() is deprecated -- use Item.id');
|
||||
|
@ -2792,31 +2793,6 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
|
|||
};
|
||||
}
|
||||
|
||||
// Update file existence state of this item
|
||||
// and best attachment state of parent item
|
||||
var self = this;
|
||||
var updateAttachmentStates = function (exists) {
|
||||
self._fileExists = exists;
|
||||
|
||||
if (self.isTopLevelItem()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var parentKey = self.getSource();
|
||||
}
|
||||
// This can happen during classic sync conflict resolution, if a
|
||||
// standalone attachment was modified locally and remotely was changed
|
||||
// into a child attachment
|
||||
catch (e) {
|
||||
Zotero.debug("Attachment parent doesn't exist for source key "
|
||||
+ "in Zotero.Item.updateAttachmentStates()", 1);
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.Items.get(parentKey).updateBestAttachmentState();
|
||||
};
|
||||
|
||||
// No associated files for linked URLs
|
||||
if (row.linkMode == Zotero.Attachments.LINK_MODE_LINKED_URL) {
|
||||
return false;
|
||||
|
@ -2824,7 +2800,7 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
|
|||
|
||||
if (!row.path) {
|
||||
Zotero.debug("Attachment path is empty", 2);
|
||||
updateAttachmentStates(false);
|
||||
this._updateAttachmentStates(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -2873,7 +2849,7 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
|
|||
}
|
||||
catch (e) {
|
||||
Zotero.debug('Invalid persistent descriptor', 2);
|
||||
updateAttachmentStates(false);
|
||||
this._updateAttachmentStates(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2883,7 +2859,7 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
|
|||
row.path.indexOf(Zotero.Attachments.BASE_PATH_PLACEHOLDER) == 0) {
|
||||
var file = Zotero.Attachments.resolveRelativePath(row.path);
|
||||
if (!file) {
|
||||
updateAttachmentStates(false);
|
||||
this._updateAttachmentStates(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2909,7 +2885,7 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
|
|||
}
|
||||
catch (e) {
|
||||
Zotero.debug('Invalid relative descriptor', 2);
|
||||
updateAttachmentStates(false);
|
||||
this._updateAttachmentStates(false);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -2917,15 +2893,41 @@ Zotero.Item.prototype.getFile = function(row, skipExistsCheck) {
|
|||
|
||||
if (!skipExistsCheck && !file.exists()) {
|
||||
Zotero.debug("Attachment file '" + file.path + "' not found", 2);
|
||||
updateAttachmentStates(false);
|
||||
this._updateAttachmentStates(false);
|
||||
return false;
|
||||
}
|
||||
|
||||
updateAttachmentStates(true);
|
||||
this._updateAttachmentStates(true);
|
||||
return file;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update file existence state of this item and best attachment state of parent item
|
||||
*/
|
||||
Zotero.Item.prototype._updateAttachmentStates = function (exists) {
|
||||
this._fileExists = exists;
|
||||
|
||||
if (this.isTopLevelItem()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
var parentKey = this.getSource();
|
||||
}
|
||||
// This can happen during classic sync conflict resolution, if a
|
||||
// standalone attachment was modified locally and remotely was changed
|
||||
// into a child attachment
|
||||
catch (e) {
|
||||
Zotero.debug("Attachment parent doesn't exist for source key "
|
||||
+ "in Zotero.Item.updateAttachmentStates()", 1);
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.Items.get(parentKey).updateBestAttachmentState();
|
||||
}
|
||||
|
||||
|
||||
Zotero.Item.prototype.getFilename = function () {
|
||||
if (!this.isAttachment()) {
|
||||
throw ("getFileName() can only be called on attachment items in Zotero.Item.getFilename()");
|
||||
|
|
|
@ -36,7 +36,7 @@ Zotero.Libraries = new function () {
|
|||
break;
|
||||
|
||||
default:
|
||||
throw ("Invalid library type '" + type + "' in Zotero.Libraries.add()");
|
||||
throw new Error("Invalid library type '" + type + "' in Zotero.Libraries.add()");
|
||||
}
|
||||
|
||||
var sql = "INSERT INTO libraries (libraryID, libraryType) VALUES (?, ?)";
|
||||
|
@ -57,7 +57,7 @@ Zotero.Libraries = new function () {
|
|||
return group.name;
|
||||
|
||||
default:
|
||||
throw ("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
|
||||
throw new Error("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -69,7 +69,7 @@ Zotero.Libraries = new function () {
|
|||
var sql = "SELECT libraryType FROM libraries WHERE libraryID=?";
|
||||
var libraryType = Zotero.DB.valueQuery(sql, libraryID);
|
||||
if (!libraryType) {
|
||||
throw ("Library " + libraryID + " does not exist in Zotero.Libraries.getType()");
|
||||
throw new Error("Library " + libraryID + " does not exist in Zotero.Libraries.getType()");
|
||||
}
|
||||
return libraryType;
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ Zotero.Libraries = new function () {
|
|||
return group.editable;
|
||||
|
||||
default:
|
||||
throw ("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
|
||||
throw new Error("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -104,7 +104,7 @@ Zotero.Libraries = new function () {
|
|||
return group.filesEditable;
|
||||
|
||||
default:
|
||||
throw ("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
|
||||
throw new Error("Unsupported library type '" + type + "' in Zotero.Libraries.getName()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1553,7 +1553,7 @@ Zotero.Integration.Fields.prototype._updateDocument = function(forceCitations, f
|
|||
} catch(e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
yield;
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
var citation = this._session.citationsByIndex[i];
|
||||
|
@ -1661,7 +1661,7 @@ Zotero.Integration.Fields.prototype._updateDocument = function(forceCitations, f
|
|||
} catch(e) {
|
||||
Zotero.logError(e);
|
||||
}
|
||||
yield;
|
||||
yield undefined;
|
||||
}
|
||||
|
||||
if(bibliographyText) {
|
||||
|
@ -2649,7 +2649,7 @@ Zotero.Integration.Session.prototype._updateCitations = function() {
|
|||
}
|
||||
this.citeprocCitationIDs[citation.citationID] = true;
|
||||
delete this.newIndices[index];
|
||||
yield;
|
||||
yield undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -2990,7 +2990,15 @@ Zotero.ItemTreeView.prototype.drop = function(row, orient)
|
|||
|
||||
try {
|
||||
Zotero.DB.beginTransaction();
|
||||
if (dragData.dropEffect == 'link') {
|
||||
var itemID = Zotero.Attachments.linkFromFile(file, sourceItemID);
|
||||
}
|
||||
else {
|
||||
if (dragData.dropEffect != 'copy') {
|
||||
Components.utils.reportError("Invalid dropEffect '" + dragData.dropEffect + "' dropping file");
|
||||
}
|
||||
var itemID = Zotero.Attachments.importFromFile(file, sourceItemID, targetLibraryID);
|
||||
}
|
||||
if (parentCollectionID) {
|
||||
var col = Zotero.Collections.get(parentCollectionID);
|
||||
if (col) {
|
||||
|
@ -3012,21 +3020,46 @@ Zotero.ItemTreeView.prototype.drop = function(row, orient)
|
|||
}
|
||||
|
||||
Zotero.ItemTreeView.prototype.onDragEnter = function (event) {
|
||||
//Zotero.debug("Storing current drag data");
|
||||
Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by HTML 5 Drag and Drop when dragging over the tree
|
||||
*/
|
||||
Zotero.ItemTreeView.prototype.onDragOver = function (event, dropdata, session) {
|
||||
Zotero.ItemTreeView.prototype.onDragOver = function (event) {
|
||||
Zotero.DragDrop.currentDataTransfer = event.dataTransfer;
|
||||
if (event.dataTransfer.types.contains("application/x-moz-file")) {
|
||||
// As of Aug. 2013 nightlies:
|
||||
//
|
||||
// - Setting the dropEffect only works on Linux and OS X.
|
||||
//
|
||||
// - Modifier keys don't show up in the drag event on OS X until the
|
||||
// drop, so since we can't show a correct effect, we leave it at
|
||||
// the default 'move', the least misleading option.
|
||||
//
|
||||
// - The cursor effect gets set by the system on Windows 7 and can't
|
||||
// be overridden.
|
||||
if (!Zotero.isMac) {
|
||||
if (event.ctrlKey && event.shiftKey) {
|
||||
event.dataTransfer.dropEffect = "link";
|
||||
}
|
||||
else {
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
}
|
||||
}
|
||||
// Show copy symbol when dragging an item over a collection
|
||||
else if (event.dataTransfer.getData("zotero/item")) {
|
||||
event.dataTransfer.dropEffect = "copy";
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
* Called by HTML 5 Drag and Drop when dropping onto the tree
|
||||
*/
|
||||
Zotero.ItemTreeView.prototype.onDrop = function (event, dropdata, session) {
|
||||
Zotero.ItemTreeView.prototype.onDrop = function (event) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ Zotero.Notifier = new function(){
|
|||
var _observers = {};
|
||||
var _disabled = false;
|
||||
var _types = [
|
||||
'collection', 'creator', 'search', 'share', 'share-items', 'item',
|
||||
'collection', 'creator', 'search', 'share', 'share-items', 'item', 'file',
|
||||
'collection-item', 'item-tag', 'tag', 'setting', 'group', 'trash', 'bucket', 'relation'
|
||||
];
|
||||
var _inTransaction;
|
||||
|
|
|
@ -75,17 +75,23 @@ Zotero.Sync.Storage = new function () {
|
|||
}
|
||||
}
|
||||
|
||||
Zotero.Notifier.registerObserver(this, ['file']);
|
||||
|
||||
|
||||
//
|
||||
// Private properties
|
||||
//
|
||||
var _maxCheckAgeInSeconds = 10800; // maximum age for upload modification check (3 hours)
|
||||
var _syncInProgress;
|
||||
var _updatesInProgress;
|
||||
var _itemDownloadPercentages = {};
|
||||
var _uploadCheckFiles = [];
|
||||
var _lastFullFileCheck = {};
|
||||
|
||||
|
||||
this.sync = function (libraries) {
|
||||
if (libraries) {
|
||||
Zotero.debug("Starting file sync for libraries " + libraries);
|
||||
this.sync = function (options) {
|
||||
if (options.libraries) {
|
||||
Zotero.debug("Starting file sync for libraries " + options.libraries);
|
||||
}
|
||||
else {
|
||||
Zotero.debug("Starting file sync");
|
||||
|
@ -100,7 +106,7 @@ Zotero.Sync.Storage = new function () {
|
|||
return Q.fcall(function () {
|
||||
// TODO: Make sure modes are active
|
||||
|
||||
if (libraries && libraries.indexOf(0) == -1) {
|
||||
if (options.libraries && options.libraries.indexOf(0) == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -116,7 +122,7 @@ Zotero.Sync.Storage = new function () {
|
|||
if (Zotero.Sync.Storage.ZFS.includeGroupFiles) {
|
||||
var groups = Zotero.Groups.getAll();
|
||||
for each(var group in groups) {
|
||||
if (libraries && libraries.indexOf(group.libraryID) == -1) {
|
||||
if (options.libraries && options.libraries.indexOf(group.libraryID) == -1) {
|
||||
continue;
|
||||
}
|
||||
// TODO: if library file syncing enabled
|
||||
|
@ -137,7 +143,7 @@ Zotero.Sync.Storage = new function () {
|
|||
Zotero.debug("WebDAV file sync is not active");
|
||||
var promise = Zotero.Sync.Storage.checkServerPromise(Zotero.Sync.Storage.WebDAV)
|
||||
.then(function () {
|
||||
mode.cacheCredentials();
|
||||
return mode.cacheCredentials();
|
||||
});
|
||||
}
|
||||
else {
|
||||
|
@ -146,6 +152,7 @@ Zotero.Sync.Storage = new function () {
|
|||
promises.push(Q.allResolved([mode, promise]));
|
||||
}
|
||||
}
|
||||
|
||||
return Q.all(promises)
|
||||
// Get library last-sync times
|
||||
.then(function (cacheCredentialsPromises) {
|
||||
|
@ -212,12 +219,42 @@ Zotero.Sync.Storage = new function () {
|
|||
}
|
||||
});
|
||||
|
||||
// Queue files to download and upload from each library
|
||||
// Check for updated files to upload in each library
|
||||
var promises = [];
|
||||
for (let libraryID in librarySyncTimes) {
|
||||
var lastSyncTime = librarySyncTimes[libraryID];
|
||||
let promise;
|
||||
libraryID = parseInt(libraryID);
|
||||
|
||||
self.checkForUpdatedFiles(null, libraryID);
|
||||
if (!Zotero.Libraries.isFilesEditable(libraryID)) {
|
||||
Zotero.debug("No file editing access -- skipping file "
|
||||
+ "modification check for library " + libraryID);
|
||||
continue;
|
||||
}
|
||||
// If this is a background sync, it's not the first sync of
|
||||
// the session, the library has had at least one full check
|
||||
// this session, and it's been less than _maxCheckAgeInSeconds
|
||||
// since the last full check of this library, check only files
|
||||
// that were previously modified or opened recently
|
||||
else if (options.background
|
||||
&& !options.firstInSession
|
||||
&& _lastFullFileCheck[libraryID]
|
||||
&& (_lastFullFileCheck[libraryID] + (_maxCheckAgeInSeconds * 1000))
|
||||
> new Date().getTime()) {
|
||||
let itemIDs = _getFilesToCheck(libraryID);
|
||||
promise = self.checkForUpdatedFiles(libraryID, itemIDs);
|
||||
}
|
||||
// Otherwise check all files in the library
|
||||
else {
|
||||
_lastFullFileCheck[libraryID] = new Date().getTime();
|
||||
promise = self.checkForUpdatedFiles(libraryID);
|
||||
}
|
||||
promises.push(promise);
|
||||
}
|
||||
return Q.all(promises)
|
||||
.then(function () {
|
||||
// Queue files to download and upload from each library
|
||||
for (let libraryID in librarySyncTimes) {
|
||||
libraryID = parseInt(libraryID);
|
||||
|
||||
var downloadAll = self.downloadOnSync(libraryID);
|
||||
|
||||
|
@ -237,6 +274,7 @@ Zotero.Sync.Storage = new function () {
|
|||
// downloads if the last sync time hasn't changed
|
||||
// or doesn't exist on the server (meaning there are no files)
|
||||
if (downloadAll && !downloadForced) {
|
||||
let lastSyncTime = librarySyncTimes[libraryID];
|
||||
if (lastSyncTime) {
|
||||
var version = self.getStoredLastSyncTime(
|
||||
libraryModes[libraryID], libraryID
|
||||
|
@ -285,6 +323,7 @@ Zotero.Sync.Storage = new function () {
|
|||
|
||||
// The promise is done when all libraries are done
|
||||
return Q.all(libraryQueues);
|
||||
});
|
||||
})
|
||||
.then(function (promises) {
|
||||
Zotero.debug('Queue manager is finished');
|
||||
|
@ -642,36 +681,65 @@ Zotero.Sync.Storage = new function () {
|
|||
* Scans local files and marks any that have changed for uploading
|
||||
* and any that are missing for downloading
|
||||
*
|
||||
* @param {Integer} [libraryID]
|
||||
* @param {Integer[]} [itemIDs]
|
||||
* @param {Object} [itemModTimes] Item mod times indexed by item ids;
|
||||
* items with stored mod times
|
||||
* that differ from the provided
|
||||
* time but file mod times
|
||||
* matching the stored time will
|
||||
* be marked for download
|
||||
* @param {Boolean} [includePersonalItems=false]
|
||||
* @param {Boolean} [includeGroupItems=false]
|
||||
* @return {Boolean} TRUE if any items changed state,
|
||||
* @return {Promise} Promise resolving to TRUE if any items changed state,
|
||||
* FALSE otherwise
|
||||
*/
|
||||
this.checkForUpdatedFiles = function (itemModTimes, libraryID) {
|
||||
this.checkForUpdatedFiles = function (libraryID, itemIDs, itemModTimes) {
|
||||
libraryID = parseInt(libraryID);
|
||||
if (isNaN(libraryID)) {
|
||||
libraryID = false;
|
||||
}
|
||||
|
||||
Components.utils.import("resource://gre/modules/Task.jsm");
|
||||
return Q(Task.spawn(function () {
|
||||
var msg = "Checking for locally changed attachment files";
|
||||
|
||||
if (typeof libraryID != 'undefined') {
|
||||
msg += " in library " + libraryID;
|
||||
if (itemModTimes) {
|
||||
throw new Error("libraryID is not allowed when itemModTimes is set");
|
||||
var memmgr = Components.classes["@mozilla.org/memory-reporter-manager;1"]
|
||||
.getService(Components.interfaces.nsIMemoryReporterManager);
|
||||
memmgr.init();
|
||||
Zotero.debug("Memory usage: " + memmgr.resident);
|
||||
|
||||
if (libraryID !== false) {
|
||||
if (itemIDs) {
|
||||
if (!itemIDs.length) {
|
||||
var msg = "No files to check for local changes in library " + libraryID;
|
||||
Zotero.debug(msg);
|
||||
throw new Task.Result(false);
|
||||
}
|
||||
}
|
||||
if (itemModTimes) {
|
||||
throw new Error("itemModTimes is not allowed when libraryID is set");
|
||||
}
|
||||
|
||||
msg += " in library " + libraryID;
|
||||
}
|
||||
else if (itemIDs) {
|
||||
throw new Error("libraryID not provided");
|
||||
}
|
||||
else if (itemModTimes) {
|
||||
if (!Object.keys(itemModTimes).length) {
|
||||
throw new Task.Result(false);
|
||||
}
|
||||
msg += " in download-marking mode";
|
||||
}
|
||||
else {
|
||||
if (!itemModTimes) {
|
||||
return false;
|
||||
}
|
||||
throw new Error("libraryID, itemIDs, or itemModTimes must be provided");
|
||||
}
|
||||
Zotero.debug(msg);
|
||||
|
||||
var changed = false;
|
||||
|
||||
var itemIDs = Object.keys(itemModTimes ? itemModTimes : {});
|
||||
if (!itemIDs) {
|
||||
itemIDs = Object.keys(itemModTimes ? itemModTimes : {});
|
||||
}
|
||||
|
||||
// Can only handle 999 bound parameters at a time
|
||||
var numIDs = itemIDs.length;
|
||||
|
@ -693,7 +761,7 @@ Zotero.Sync.Storage = new function () {
|
|||
Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD,
|
||||
Zotero.Sync.Storage.SYNC_STATE_IN_SYNC
|
||||
);
|
||||
if (typeof libraryID != 'undefined') {
|
||||
if (libraryID !== false) {
|
||||
sql += " AND libraryID=?";
|
||||
params.push(libraryID == 0 ? null : libraryID);
|
||||
}
|
||||
|
@ -709,22 +777,23 @@ Zotero.Sync.Storage = new function () {
|
|||
}
|
||||
while (done < numIDs);
|
||||
|
||||
Zotero.DB.commitTransaction();
|
||||
|
||||
// If no files, or everything is already marked for download,
|
||||
// we don't need to do anything
|
||||
if (!rows.length) {
|
||||
var msg = "No in-sync or to-upload files found";
|
||||
if (typeof libraryID != 'undefined') {
|
||||
if (libraryID !== false) {
|
||||
msg += " in library " + libraryID;
|
||||
}
|
||||
Zotero.debug(msg);
|
||||
Zotero.DB.commitTransaction();
|
||||
return changed;
|
||||
throw new Task.Result(changed);
|
||||
}
|
||||
|
||||
// Index attachment data by item id
|
||||
var itemIDs = [];
|
||||
itemIDs = [];
|
||||
var attachmentData = {};
|
||||
for each(var row in rows) {
|
||||
for each(let row in rows) {
|
||||
var id = row.itemID;
|
||||
itemIDs.push(id);
|
||||
attachmentData[id] = {
|
||||
|
@ -737,12 +806,26 @@ Zotero.Sync.Storage = new function () {
|
|||
}
|
||||
rows = null;
|
||||
|
||||
var updatedStates = {};
|
||||
var t = new Date();
|
||||
var items = Zotero.Items.get(itemIDs);
|
||||
var numItems = items.length;
|
||||
var updatedStates = {};
|
||||
|
||||
// OS.File didn't work reliably before Firefox 23, so use the old code
|
||||
if (Zotero.platformMajorVersion < 23) {
|
||||
Zotero.debug("Performing synchronous file update check");
|
||||
|
||||
for each(var item in items) {
|
||||
var lk = libraryID + "/" + item.key;
|
||||
//Zotero.debug("Checking attachment file for item " + lk);
|
||||
var file = item.getFile(attachmentData[item.id]);
|
||||
// Spin the event loop during synchronous file access
|
||||
yield Q.delay(1);
|
||||
|
||||
Zotero.debug("Memory usage: " + memmgr.resident);
|
||||
|
||||
let row = attachmentData[item.id];
|
||||
let lk = item.libraryID + "/" + item.key;
|
||||
Zotero.debug("Checking attachment file for item " + lk);
|
||||
|
||||
var file = item.getFile(row);
|
||||
if (!file) {
|
||||
Zotero.debug("Marking attachment " + lk + " as missing");
|
||||
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
|
||||
|
@ -753,13 +836,13 @@ Zotero.Sync.Storage = new function () {
|
|||
// is download-marking mode (itemModTimes) and the file was
|
||||
// changed remotely, conflicts are checked at upload time, so we
|
||||
// don't need to worry about it here.
|
||||
if (attachmentData[item.id].state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
|
||||
if (row.state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
|
||||
continue;
|
||||
}
|
||||
|
||||
var fmtime = item.attachmentModificationTime;
|
||||
|
||||
//Zotero.debug("Stored mtime is " + attachmentData[item.id].mtime);
|
||||
//Zotero.debug("Stored mtime is " + row.mtime);
|
||||
//Zotero.debug("File mtime is " + fmtime);
|
||||
|
||||
// Download-marking mode
|
||||
|
@ -777,7 +860,7 @@ Zotero.Sync.Storage = new function () {
|
|||
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD;
|
||||
}
|
||||
|
||||
var mtime = attachmentData[item.id].mtime;
|
||||
var mtime = row.mtime;
|
||||
|
||||
// If stored time matches file, it hasn't changed locally
|
||||
if (mtime == fmtime) {
|
||||
|
@ -814,12 +897,12 @@ Zotero.Sync.Storage = new function () {
|
|||
Zotero.debug("File for item " + lk + " missing before getting hash");
|
||||
}
|
||||
var fileHash = item.attachmentHash;
|
||||
if (attachmentData[item.id].hash && attachmentData[item.id].hash == fileHash) {
|
||||
if (row.hash && row.hash == fileHash) {
|
||||
Zotero.debug("Mod time didn't match (" + fmtime + "!=" + mtime + ") "
|
||||
+ "but hash did for " + file.leafName + " for item " + lk
|
||||
+ " -- updating file mod time");
|
||||
try {
|
||||
file.lastModifiedTime = attachmentData[item.id].mtime;
|
||||
file.lastModifiedTime = row.mtime;
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.File.checkFileAccessError(e, file, 'update');
|
||||
|
@ -842,10 +925,162 @@ Zotero.Sync.Storage = new function () {
|
|||
Zotero.debug("No synced files have changed locally");
|
||||
}
|
||||
|
||||
Zotero.DB.commitTransaction();
|
||||
return changed;
|
||||
Zotero.debug("Checked " + numItems + " files in " + (new Date() - t) + "ms");
|
||||
|
||||
throw new Task.Result(changed);
|
||||
}
|
||||
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm")
|
||||
|
||||
let checkItems = function () {
|
||||
if (!items.length) return;
|
||||
|
||||
Zotero.debug("Memory usage: " + memmgr.resident);
|
||||
|
||||
let item = items.shift();
|
||||
let row = attachmentData[item.id];
|
||||
let lk = item.libraryKey;
|
||||
Zotero.debug("Checking attachment file for item " + lk);
|
||||
|
||||
let nsIFile = item.getFile(row, true);
|
||||
let file = null;
|
||||
return Q(OS.File.open(nsIFile.path))
|
||||
.then(function (promisedFile) {
|
||||
file = promisedFile;
|
||||
return file.stat()
|
||||
.then(function (info) {
|
||||
Zotero.debug("Memory usage: " + memmgr.resident);
|
||||
|
||||
var fmtime = info.lastModificationDate.getTime();
|
||||
Zotero.debug("File modification time for item " + lk + " is " + fmtime);
|
||||
|
||||
if (fmtime < 1) {
|
||||
Zotero.debug("File mod time " + fmtime + " is less than 1 -- interpreting as 1", 2);
|
||||
fmtime = 1;
|
||||
}
|
||||
|
||||
// If file is already marked for upload, skip check. Even if this
|
||||
// is download-marking mode (itemModTimes) and the file was
|
||||
// changed remotely, conflicts are checked at upload time, so we
|
||||
// don't need to worry about it here.
|
||||
if (row.state == Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Zotero.debug("Stored mtime is " + row.mtime);
|
||||
//Zotero.debug("File mtime is " + fmtime);
|
||||
|
||||
// Download-marking mode
|
||||
if (itemModTimes) {
|
||||
Zotero.debug("Remote mod time for item " + lk + " is " + itemModTimes[item.id]);
|
||||
|
||||
// Ignore attachments whose stored mod times haven't changed
|
||||
if (row.storageModTime == itemModTimes[id]) {
|
||||
Zotero.debug("Storage mod time (" + row.storageModTime + ") "
|
||||
+ "hasn't changed for item " + lk);
|
||||
return;
|
||||
}
|
||||
|
||||
Zotero.debug("Marking attachment " + lk + " for download");
|
||||
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_FORCE_DOWNLOAD;
|
||||
}
|
||||
|
||||
var mtime = row.mtime;
|
||||
|
||||
// If stored time matches file, it hasn't changed locally
|
||||
if (mtime == fmtime) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow floored timestamps for filesystems that don't support
|
||||
// millisecond precision (e.g., HFS+)
|
||||
if (Math.floor(mtime / 1000) * 1000 == fmtime || Math.floor(fmtime / 1000) * 1000 == mtime) {
|
||||
Zotero.debug("File mod times are within one-second precision "
|
||||
+ "(" + fmtime + " ≅ " + mtime + ") for " + file.leafName
|
||||
+ " for item " + lk + " -- ignoring");
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow timestamp to be exactly one hour off to get around
|
||||
// time zone issues -- there may be a proper way to fix this
|
||||
if (Math.abs(fmtime - mtime) == 3600000
|
||||
// And check with one-second precision as well
|
||||
|| Math.abs(fmtime - Math.floor(mtime / 1000) * 1000) == 3600000
|
||||
|| Math.abs(Math.floor(fmtime / 1000) * 1000 - mtime) == 3600000) {
|
||||
Zotero.debug("File mod time (" + fmtime + ") is exactly one "
|
||||
+ "hour off remote file (" + mtime + ") for item " + lk
|
||||
+ "-- assuming time zone issue and skipping upload");
|
||||
return;
|
||||
}
|
||||
|
||||
// If file hash matches stored hash, only the mod time changed, so skip
|
||||
return Zotero.Utilities.Internal.md5Async(file)
|
||||
.then(function (fileHash) {
|
||||
if (row.hash && row.hash == fileHash) {
|
||||
Zotero.debug("Mod time didn't match (" + fmtime + "!=" + mtime + ") "
|
||||
+ "but hash did for " + file.leafName + " for item " + lk
|
||||
+ " -- updating file mod time");
|
||||
try {
|
||||
nsIFile.lastModifiedTime = row.mtime;
|
||||
}
|
||||
catch (e) {
|
||||
Zotero.File.checkFileAccessError(e, nsIFile, 'update');
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// Mark file for upload
|
||||
Zotero.debug("Marking attachment " + lk + " as changed "
|
||||
+ "(" + mtime + " != " + fmtime + ")");
|
||||
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_UPLOAD;
|
||||
});
|
||||
});
|
||||
})
|
||||
.finally(function () {
|
||||
if (file) {
|
||||
Zotero.debug("Closing file for item " + lk);
|
||||
file.close();
|
||||
}
|
||||
})
|
||||
.catch(function (e) {
|
||||
if (e instanceof OS.File.Error && e.becauseNoSuchFile) {
|
||||
Zotero.debug("Marking attachment " + lk + " as missing");
|
||||
updatedStates[item.id] = Zotero.Sync.Storage.SYNC_STATE_TO_DOWNLOAD;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e instanceof OS.File.Error && e.becauseClosed) {
|
||||
Zotero.debug("File was closed", 2);
|
||||
}
|
||||
else {
|
||||
Zotero.debug(e);
|
||||
Zotero.debug(e.toString());
|
||||
}
|
||||
throw new Error("Error " + e.operation + " " + nsIFile.path);
|
||||
})
|
||||
.then(function () {
|
||||
return checkItems();
|
||||
});
|
||||
};
|
||||
|
||||
throw new Task.Result(checkItems()
|
||||
.then(function () {
|
||||
for (let itemID in updatedStates) {
|
||||
Zotero.Sync.Storage.setSyncState(itemID, updatedStates[itemID]);
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (!changed) {
|
||||
Zotero.debug("No synced files have changed locally");
|
||||
}
|
||||
|
||||
Zotero.debug("Checked " + numItems + " files in " + (new Date() - t) + "ms");
|
||||
|
||||
return changed;
|
||||
}));
|
||||
}));
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Download a single file
|
||||
|
@ -1144,6 +1379,20 @@ Zotero.Sync.Storage = new function () {
|
|||
}
|
||||
|
||||
|
||||
this.notify = function(event, type, ids, extraData) {
|
||||
if (event == 'open' && type == 'file') {
|
||||
let timestamp = new Date().getTime();
|
||||
|
||||
for each(let id in ids) {
|
||||
_uploadCheckFiles.push({
|
||||
itemID: id,
|
||||
timestamp: timestamp
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// Private methods
|
||||
//
|
||||
|
@ -1774,6 +2023,36 @@ Zotero.Sync.Storage = new function () {
|
|||
}
|
||||
|
||||
|
||||
/**
|
||||
* Get files to check for local modifications for uploading
|
||||
*
|
||||
* This includes files previously modified and files opened externally
|
||||
* via Zotero within _maxCheckAgeInSeconds.
|
||||
*/
|
||||
function _getFilesToCheck(libraryID) {
|
||||
var minTime = new Date().getTime() - (_maxCheckAgeInSeconds * 1000);
|
||||
|
||||
// Get files by modification time
|
||||
var sql = "SELECT itemID FROM itemAttachments JOIN items USING (itemID) "
|
||||
+ "WHERE libraryID=? AND linkMode IN (?,?) AND syncState IN (?) AND "
|
||||
+ "storageModTime>=?";
|
||||
var params = [
|
||||
libraryID == 0 ? null : libraryID,
|
||||
Zotero.Attachments.LINK_MODE_IMPORTED_FILE,
|
||||
Zotero.Attachments.LINK_MODE_IMPORTED_URL,
|
||||
Zotero.Sync.Storage.SYNC_STATE_IN_SYNC,
|
||||
minTime
|
||||
];
|
||||
var itemIDs = Zotero.DB.columnQuery(sql, params) || [];
|
||||
|
||||
// Get files by open time
|
||||
_uploadCheckFiles.filter(function (x) x.timestamp >= minTime);
|
||||
itemIDs = itemIDs.concat([x.itemID for each(x in _uploadCheckFiles)])
|
||||
|
||||
return Zotero.Utilities.arrayUnique(itemIDs);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @inner
|
||||
* @return {String[]|FALSE} Array of keys, or FALSE if none
|
||||
|
|
|
@ -74,8 +74,8 @@ Zotero.Sync.Storage.Mode.prototype.checkServerCallback = function (uri, status,
|
|||
return this._checkServerCallback(uri, status, window, skipSuccessMessage);
|
||||
}
|
||||
|
||||
Zotero.Sync.Storage.Mode.prototype.cacheCredentials = function (callback) {
|
||||
return this._cacheCredentials(callback);
|
||||
Zotero.Sync.Storage.Mode.prototype.cacheCredentials = function () {
|
||||
return this._cacheCredentials();
|
||||
}
|
||||
|
||||
Zotero.Sync.Storage.Mode.prototype.purgeDeletedStorageFiles = function (callback) {
|
||||
|
|
|
@ -44,17 +44,12 @@ Zotero.Sync.Storage.StreamListener.prototype = {
|
|||
|
||||
// nsIProgressEventSink
|
||||
onProgress: function (request, context, progress, progressMax) {
|
||||
// Workaround for https://bugzilla.mozilla.org/show_bug.cgi?id=451991
|
||||
// (fixed in Fx3.1)
|
||||
if (progress > progressMax) {
|
||||
progress = progressMax;
|
||||
}
|
||||
//Zotero.debug("onProgress with " + progress + "/" + progressMax);
|
||||
Zotero.debug("onProgress with " + progress + "/" + progressMax);
|
||||
this._onProgress(request, progress, progressMax);
|
||||
},
|
||||
|
||||
onStatus: function (request, context, status, statusArg) {
|
||||
//Zotero.debug('onStatus');
|
||||
Zotero.debug('onStatus with ' + status);
|
||||
},
|
||||
|
||||
// nsIRequestObserver
|
||||
|
@ -67,7 +62,7 @@ Zotero.Sync.Storage.StreamListener.prototype = {
|
|||
},
|
||||
|
||||
onStopRequest: function (request, context, status) {
|
||||
Zotero.debug('onStopRequest');
|
||||
Zotero.debug('onStopRequest with ' + status);
|
||||
|
||||
switch (status) {
|
||||
case 0:
|
||||
|
@ -84,7 +79,7 @@ Zotero.Sync.Storage.StreamListener.prototype = {
|
|||
// nsIWebProgressListener
|
||||
onProgressChange: function (wp, request, curSelfProgress,
|
||||
maxSelfProgress, curTotalProgress, maxTotalProgress) {
|
||||
//Zotero.debug("onProgressChange with " + curTotalProgress + "/" + maxTotalProgress);
|
||||
Zotero.debug("onProgressChange with " + curTotalProgress + "/" + maxTotalProgress);
|
||||
|
||||
// onProgress gets called too, so this isn't necessary
|
||||
//this._onProgress(request, curTotalProgress, maxTotalProgress);
|
||||
|
@ -108,8 +103,12 @@ Zotero.Sync.Storage.StreamListener.prototype = {
|
|||
onStatusChange: function (progress, request, status, message) {
|
||||
Zotero.debug("onStatusChange with '" + message + "'");
|
||||
},
|
||||
onLocationChange: function () {},
|
||||
onSecurityChange: function () {},
|
||||
onLocationChange: function () {
|
||||
Zotero.debug('onLocationChange');
|
||||
},
|
||||
onSecurityChange: function () {
|
||||
Zotero.debug('onSecurityChange');
|
||||
},
|
||||
|
||||
// nsIStreamListener
|
||||
onDataAvailable: function (request, context, stream, sourceOffset, length) {
|
||||
|
@ -119,7 +118,9 @@ Zotero.Sync.Storage.StreamListener.prototype = {
|
|||
.createInstance(Components.interfaces.nsIScriptableInputStream);
|
||||
scriptableInputStream.init(stream);
|
||||
|
||||
this._response += scriptableInputStream.read(length);
|
||||
var data = scriptableInputStream.read(length);
|
||||
Zotero.debug(data);
|
||||
this._response += data;
|
||||
},
|
||||
|
||||
// nsIChannelEventSink
|
||||
|
|
|
@ -1102,7 +1102,7 @@ Zotero.Sync.Storage.WebDAV = (function () {
|
|||
|
||||
obj._cacheCredentials = function () {
|
||||
if (_cachedCredentials) {
|
||||
Zotero.debug("Credentials are already cached");
|
||||
Zotero.debug("WebDAV credentials are already cached");
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ Zotero.Sync.Storage.ZFS = (function () {
|
|||
}
|
||||
else {
|
||||
var msg = "Unexpected status code " + e.xmlhttp.status
|
||||
+ " getting storage file info";
|
||||
+ " getting storage file info for item " + item.libraryKey;
|
||||
}
|
||||
Zotero.debug(msg, 1);
|
||||
Zotero.debug(e.xmlhttp.responseText);
|
||||
|
@ -1002,8 +1002,8 @@ Zotero.Sync.Storage.ZFS = (function () {
|
|||
|
||||
obj._cacheCredentials = function () {
|
||||
if (_cachedCredentials) {
|
||||
Zotero.debug("Credentials are already cached");
|
||||
return;
|
||||
Zotero.debug("ZFS credentials are already cached");
|
||||
return Q();
|
||||
}
|
||||
|
||||
var uri = this.rootURI;
|
||||
|
|
|
@ -520,6 +520,7 @@ Zotero.Sync.Runner = new function () {
|
|||
var _autoSyncTimer;
|
||||
var _queue;
|
||||
var _background;
|
||||
var _firstInSession = true;
|
||||
|
||||
var _lastSyncStatus;
|
||||
var _currentSyncStatusLabel;
|
||||
|
@ -533,7 +534,13 @@ Zotero.Sync.Runner = new function () {
|
|||
this.IdleListener.init();
|
||||
}
|
||||
|
||||
this.sync = function (background) {
|
||||
this.sync = function (options) {
|
||||
if (!options) options = {};
|
||||
if (_firstInSession) {
|
||||
options.firstInSession = true;
|
||||
_firstInSession = false;
|
||||
}
|
||||
|
||||
_warning = null;
|
||||
|
||||
if (Zotero.HTTP.browserIsOffline()){
|
||||
|
@ -549,7 +556,7 @@ Zotero.Sync.Runner = new function () {
|
|||
// Purge deleted objects so they don't cause sync errors (e.g., long tags)
|
||||
Zotero.purgeDataObjects(true);
|
||||
|
||||
_background = !!background;
|
||||
_background = !!options.background;
|
||||
this.setSyncIcon('animate');
|
||||
|
||||
var finalCallbacks = {
|
||||
|
@ -562,7 +569,7 @@ Zotero.Sync.Runner = new function () {
|
|||
var storageSync = function () {
|
||||
Zotero.Sync.Runner.setSyncStatus(Zotero.getString('sync.status.syncingFiles'));
|
||||
|
||||
Zotero.Sync.Storage.sync()
|
||||
Zotero.Sync.Storage.sync(options)
|
||||
.then(function (results) {
|
||||
Zotero.debug("File sync is finished");
|
||||
|
||||
|
@ -692,7 +699,9 @@ Zotero.Sync.Runner = new function () {
|
|||
return;
|
||||
}
|
||||
|
||||
Zotero.Sync.Runner.sync(background);
|
||||
Zotero.Sync.Runner.sync({
|
||||
background: background
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1143,8 +1152,10 @@ Zotero.Sync.Runner.IdleListener = {
|
|||
|
||||
Zotero.debug("Beginning idle sync");
|
||||
|
||||
Zotero.Sync.Runner.sync(true);
|
||||
Zotero.Sync.Runner.setSyncTimeout(this._idleTimeout, true);
|
||||
Zotero.Sync.Runner.sync({
|
||||
background: true
|
||||
});
|
||||
Zotero.Sync.Runner.setSyncTimeout(this._idleTimeout, true, true);
|
||||
},
|
||||
|
||||
_backObserver: {
|
||||
|
@ -1160,7 +1171,9 @@ Zotero.Sync.Runner.IdleListener = {
|
|||
return;
|
||||
}
|
||||
Zotero.debug("Beginning return-from-idle sync");
|
||||
Zotero.Sync.Runner.sync(true);
|
||||
Zotero.Sync.Runner.sync({
|
||||
background: true
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -1561,8 +1574,9 @@ Zotero.Sync.Server = new function () {
|
|||
_error(e);
|
||||
}
|
||||
|
||||
try {
|
||||
var gen = Zotero.Sync.Server.Data.processUpdatedXML(
|
||||
Components.utils.import("resource://gre/modules/Task.jsm");
|
||||
|
||||
Task.spawn(Zotero.Sync.Server.Data.processUpdatedXML(
|
||||
responseNode.getElementsByTagName('updated')[0],
|
||||
lastLocalSyncDate,
|
||||
syncSession,
|
||||
|
@ -1763,17 +1777,13 @@ Zotero.Sync.Server = new function () {
|
|||
Zotero.HTTP.doPost(url, body, uploadCallback);
|
||||
}
|
||||
}
|
||||
))
|
||||
.then(
|
||||
null,
|
||||
function (e) {
|
||||
errorHandler(e);
|
||||
}
|
||||
);
|
||||
|
||||
try {
|
||||
gen.next();
|
||||
}
|
||||
catch (e if e.toString() === "[object StopIteration]") {}
|
||||
Zotero.pumpGenerator(gen, false, errorHandler);
|
||||
}
|
||||
catch (e) {
|
||||
errorHandler(e, true);
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
_error(e);
|
||||
|
@ -2024,7 +2034,9 @@ Zotero.Sync.Server = new function () {
|
|||
|
||||
Zotero.Sync.Server.resetClient();
|
||||
Zotero.Sync.Server.canAutoResetClient = false;
|
||||
Zotero.Sync.Runner.sync(background);
|
||||
Zotero.Sync.Runner.sync({
|
||||
background: background
|
||||
});
|
||||
}, 1);
|
||||
break;
|
||||
|
||||
|
@ -2127,7 +2139,9 @@ Zotero.Sync.Server = new function () {
|
|||
Zotero.Sync.Server.canAutoResetClient = false;
|
||||
}
|
||||
|
||||
Zotero.Sync.Runner.sync(background);
|
||||
Zotero.Sync.Runner.sync({
|
||||
background: background
|
||||
});
|
||||
}, 1);
|
||||
break;
|
||||
|
||||
|
@ -2362,7 +2376,9 @@ Zotero.Sync.Server = new function () {
|
|||
}
|
||||
Zotero.Sync.Server.resetClient();
|
||||
Zotero.Sync.Server.canAutoResetClient = false;
|
||||
Zotero.Sync.Runner.sync(background);
|
||||
Zotero.Sync.Runner.sync({
|
||||
background: background
|
||||
});
|
||||
}, 1);
|
||||
break;
|
||||
|
||||
|
@ -3433,14 +3449,14 @@ Zotero.Sync.Server.Data = new function() {
|
|||
// 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 (type == 'item' && Object.keys(itemStorageModTimes).length) {
|
||||
Zotero.Sync.Storage.checkForUpdatedFiles(itemStorageModTimes);
|
||||
yield Zotero.Sync.Storage.checkForUpdatedFiles(null, null, itemStorageModTimes);
|
||||
}
|
||||
}
|
||||
|
||||
if (_timeToYield()) yield true;
|
||||
|
||||
callback(Zotero.Sync.Server.Data.buildUploadXML(syncSession));
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
|
|
|
@ -80,9 +80,6 @@ Zotero.Utilities.Internal = {
|
|||
return hash;
|
||||
}
|
||||
|
||||
/*
|
||||
// This created 36-character hashes
|
||||
|
||||
// return the two-digit hexadecimal code for a byte
|
||||
function toHexString(charCode) {
|
||||
return ("0" + charCode.toString(16)).slice(-2);
|
||||
|
@ -90,18 +87,98 @@ Zotero.Utilities.Internal = {
|
|||
|
||||
// convert the binary hash data to a hex string.
|
||||
return [toHexString(hash.charCodeAt(i)) for (i in hash)].join("");
|
||||
*/
|
||||
},
|
||||
|
||||
// From http://rcrowley.org/2007/11/15/md5-in-xulrunner-or-firefox-extensions/
|
||||
var ascii = [];
|
||||
var ii = hash.length;
|
||||
for (var i = 0; i < ii; ++i) {
|
||||
var c = hash.charCodeAt(i);
|
||||
var ones = c % 16;
|
||||
var tens = c >> 4;
|
||||
ascii.push(String.fromCharCode(tens + (tens > 9 ? 87 : 48)) + String.fromCharCode(ones + (ones > 9 ? 87 : 48)));
|
||||
|
||||
/**
|
||||
* @param {OS.File|nsIFile|String} file File or file path
|
||||
* @param {Boolean} [base64=FALSE] Return as base-64-encoded string
|
||||
* rather than hex string
|
||||
*/
|
||||
"md5Async": function (file, base64) {
|
||||
Components.utils.import("resource://gre/modules/osfile.jsm");
|
||||
const CHUNK_SIZE = 16384;
|
||||
|
||||
var deferred = Q.defer();
|
||||
|
||||
function toHexString(charCode) {
|
||||
return ("0" + charCode.toString(16)).slice(-2);
|
||||
}
|
||||
return ascii.join('');
|
||||
|
||||
var ch = Components.classes["@mozilla.org/security/hash;1"]
|
||||
.createInstance(Components.interfaces.nsICryptoHash);
|
||||
ch.init(ch.MD5);
|
||||
|
||||
// Recursively read chunks of the file, and resolve the promise
|
||||
// with the hash when done
|
||||
let readChunk = function readChunk(file) {
|
||||
file.read(CHUNK_SIZE)
|
||||
.then(
|
||||
function readSuccess(data) {
|
||||
ch.update(data, data.length);
|
||||
if (data.length == CHUNK_SIZE) {
|
||||
readChunk(file);
|
||||
}
|
||||
else {
|
||||
let hash = ch.finish(base64);
|
||||
|
||||
// Base64
|
||||
if (base64) {
|
||||
deferred.resolve(hash);
|
||||
}
|
||||
// Hex string
|
||||
else {
|
||||
deferred.resolve(
|
||||
[toHexString(hash.charCodeAt(i))
|
||||
for (i in hash)].join("")
|
||||
);
|
||||
}
|
||||
}
|
||||
},
|
||||
function (e) {
|
||||
try {
|
||||
ch.finish(false);
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
deferred.reject(e);
|
||||
}
|
||||
)
|
||||
.then(
|
||||
null,
|
||||
function (e) {
|
||||
try {
|
||||
ch.finish(false);
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
deferred.reject(e);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
if (file instanceof OS.File) {
|
||||
readChunk(file);
|
||||
}
|
||||
else {
|
||||
if (file instanceof Components.interfaces.nsIFile) {
|
||||
var path = file.path;
|
||||
}
|
||||
else {
|
||||
var path = file;
|
||||
}
|
||||
OS.File.open(path)
|
||||
.then(
|
||||
function opened(file) {
|
||||
readChunk(file);
|
||||
},
|
||||
function (e) {
|
||||
deferred.reject(e);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return deferred.promise;
|
||||
},
|
||||
|
||||
|
||||
|
|
|
@ -219,6 +219,11 @@ Components.utils.import("resource://gre/modules/Services.jsm");
|
|||
Zotero.Debug.init();
|
||||
|
||||
this.mainThread = Services.tm.mainThread;
|
||||
|
||||
var appInfo = Components.classes["@mozilla.org/xre/app-info;1"]
|
||||
.getService(Components.interfaces.nsIXULAppInfo);
|
||||
this.platformVersion = appInfo.platformVersion;
|
||||
this.platformMajorVersion = parseInt(appInfo.platformVersion.match(/^[0-9]+/)[0]);
|
||||
this.isFx = true;
|
||||
|
||||
this.isStandalone = Services.appinfo.ID == ZOTERO_CONFIG['GUID'];
|
||||
|
@ -1892,13 +1897,13 @@ Components.utils.import("resource://gre/modules/Services.jsm");
|
|||
'[JavaScript Error: "this._uiElement is null',
|
||||
'Error: a._updateVisibleText is not a function',
|
||||
'[JavaScript Error: "Warning: unrecognized command line flag ',
|
||||
'[JavaScript Error: "Warning: unrecognized command line flag -foreground',
|
||||
'LibX:',
|
||||
'function skype_',
|
||||
'[JavaScript Error: "uncaught exception: Permission denied to call method Location.toString"]',
|
||||
'CVE-2009-3555',
|
||||
'OpenGL LayerManager',
|
||||
'trying to re-register CID'
|
||||
'trying to re-register CID',
|
||||
'Services.HealthReport'
|
||||
];
|
||||
|
||||
for (var i=0; i<blacklist.length; i++) {
|
||||
|
@ -2233,6 +2238,7 @@ Zotero.Keys = new function() {
|
|||
// Get the key=>command mappings from the prefs
|
||||
for each(var action in actions) {
|
||||
var action = action.substr(5); // strips 'keys.'
|
||||
// Remove old pref
|
||||
if (action == 'overrideGlobal') {
|
||||
Zotero.Prefs.clear('keys.overrideGlobal');
|
||||
continue;
|
||||
|
@ -2246,26 +2252,35 @@ Zotero.Keys = new function() {
|
|||
* Called by ZoteroPane.onLoad()
|
||||
*/
|
||||
function windowInit(document) {
|
||||
var useShift = Zotero.isMac;
|
||||
var globalKeys = [
|
||||
{
|
||||
name: 'openZotero',
|
||||
defaultKey: 'Z'
|
||||
},
|
||||
{
|
||||
name: 'saveToZotero',
|
||||
defaultKey: 'S'
|
||||
}
|
||||
];
|
||||
|
||||
// Zotero pane shortcut
|
||||
var keyElem = document.getElementById('key_openZotero');
|
||||
if(keyElem) {
|
||||
var zKey = Zotero.Prefs.get('keys.openZotero');
|
||||
// Only override the default with the pref if the <key> hasn't been manually changed
|
||||
// and the pref has been
|
||||
if (keyElem.getAttribute('key') == 'Z' && keyElem.getAttribute('modifiers') == 'accel alt'
|
||||
&& (zKey != 'Z' || useShift)) {
|
||||
keyElem.setAttribute('key', zKey);
|
||||
if (useShift) {
|
||||
keyElem.setAttribute('modifiers', 'accel shift');
|
||||
}
|
||||
globalKeys.forEach(function (x) {
|
||||
let keyElem = document.getElementById('key_' + x.name);
|
||||
if (keyElem) {
|
||||
let prefKey = Zotero.Prefs.get('keys.' + x.name);
|
||||
// Only override the default with the pref if the <key> hasn't
|
||||
// been manually changed and the pref has been
|
||||
if (keyElem.getAttribute('key') == x.defaultKey
|
||||
&& keyElem.getAttribute('modifiers') == 'accel shift'
|
||||
&& prefKey != x.defaultKey) {
|
||||
keyElem.setAttribute('key', prefKey);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function getCommand(key) {
|
||||
key = key.toUpperCase();
|
||||
return _keys[key] ? _keys[key] : false;
|
||||
}
|
||||
}
|
||||
|
@ -2310,17 +2325,19 @@ Zotero.DragDrop = {
|
|||
currentDataTransfer: null,
|
||||
|
||||
getDragData: function (element, firstOnly) {
|
||||
var dragData = {
|
||||
dataType: '',
|
||||
data: []
|
||||
};
|
||||
|
||||
var dt = this.currentDataTransfer;
|
||||
if (!dt) {
|
||||
Zotero.debug("Drag data not available");
|
||||
return false;
|
||||
}
|
||||
|
||||
var dragData = {
|
||||
dataType: '',
|
||||
data: [],
|
||||
dropEffect: dt.dropEffect
|
||||
};
|
||||
|
||||
|
||||
var len = firstOnly ? 1 : dt.mozItemCount;
|
||||
|
||||
if (dt.types.contains('zotero/collection')) {
|
||||
|
|
|
@ -433,7 +433,9 @@ var ZoteroPane = new function()
|
|||
return;
|
||||
}
|
||||
|
||||
Zotero.Sync.Runner.sync(true);
|
||||
Zotero.Sync.Runner.sync({
|
||||
background: true
|
||||
});
|
||||
})
|
||||
.done();
|
||||
}
|
||||
|
@ -602,17 +604,14 @@ var ZoteroPane = new function()
|
|||
}
|
||||
}
|
||||
|
||||
var useShift = Zotero.isMac;
|
||||
|
||||
var key = String.fromCharCode(event.which);
|
||||
if (!key) {
|
||||
Zotero.debug('No key');
|
||||
return;
|
||||
}
|
||||
|
||||
// Ignore modifiers other than Ctrl-Alt or Cmd-Shift
|
||||
if (!((Zotero.isMac ? event.metaKey : event.ctrlKey) &&
|
||||
(useShift ? event.shiftKey : event.altKey))) {
|
||||
// Ignore modifiers other than Ctrl-Shift/Cmd-Shift
|
||||
if (!((Zotero.isMac ? event.metaKey : event.ctrlKey) && event.shiftKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -687,9 +686,7 @@ var ZoteroPane = new function()
|
|||
}
|
||||
}
|
||||
// Use key that's not the modifier as the popup toggle
|
||||
ZoteroPane_Local.newNote(
|
||||
useShift ? event.altKey : event.shiftKey, parent
|
||||
);
|
||||
ZoteroPane_Local.newNote(event.altKey, parent);
|
||||
break;
|
||||
case 'toggleTagSelector':
|
||||
ZoteroPane_Local.toggleTagSelector();
|
||||
|
@ -2563,14 +2560,8 @@ var ZoteroPane = new function()
|
|||
else if (tree.id == 'zotero-items-tree') {
|
||||
let itemGroup = ZoteroPane_Local.getItemGroup();
|
||||
if (itemGroup.isDuplicates()) {
|
||||
if (event.button == 0 && (event.metaKey || event.shiftKey
|
||||
|| event.altKey || event.ctrlKey)) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Allow right-click on single items/attachments
|
||||
var items = ZoteroPane_Local.getSelectedItems();
|
||||
if (event.button != 0 && items.length == 1) {
|
||||
if (event.button != 0 || event.metaKey || event.shiftKey
|
||||
|| event.altKey || event.ctrlKey) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -3503,6 +3494,7 @@ var ZoteroPane = new function()
|
|||
this.loadURI(url, event);
|
||||
}
|
||||
else {
|
||||
Zotero.Notifier.trigger('open', 'file', itemID);
|
||||
Zotero.launchFile(file);
|
||||
}
|
||||
}
|
||||
|
@ -3624,6 +3616,7 @@ var ZoteroPane = new function()
|
|||
var parent = file.parent.QueryInterface(Components.interfaces.nsILocalFile);
|
||||
Zotero.launchFile(parent);
|
||||
}
|
||||
Zotero.Notifier.trigger('open', 'file', attachment.id);
|
||||
}
|
||||
else {
|
||||
this.showAttachmentNotFoundDialog(attachment.id, noLocateOnMissing)
|
||||
|
|
|
@ -126,16 +126,16 @@
|
|||
<!ENTITY zotero.preferences.prefpane.keys "Shortcuts">
|
||||
|
||||
<!ENTITY zotero.preferences.keys.openZotero "Open/Close Zotero Pane">
|
||||
<!ENTITY zotero.preferences.keys.saveToZotero "Save to Zotero (address bar icon)">
|
||||
<!ENTITY zotero.preferences.keys.toggleFullscreen "Toggle Fullscreen Mode">
|
||||
<!ENTITY zotero.preferences.keys.focusLibrariesPane "Focus Libraries Pane">
|
||||
<!ENTITY zotero.preferences.keys.quicksearch "Quick Search">
|
||||
<!ENTITY zotero.preferences.keys.newItem "Create a new item">
|
||||
<!ENTITY zotero.preferences.keys.newNote "Create a new note">
|
||||
<!ENTITY zotero.preferences.keys.newItem "Create a New Item">
|
||||
<!ENTITY zotero.preferences.keys.newNote "Create a New Note">
|
||||
<!ENTITY zotero.preferences.keys.toggleTagSelector "Toggle Tag Selector">
|
||||
<!ENTITY zotero.preferences.keys.copySelectedItemCitationsToClipboard "Copy Selected Item Citations to Clipboard">
|
||||
<!ENTITY zotero.preferences.keys.copySelectedItemsToClipboard "Copy Selected Items to Clipboard">
|
||||
<!ENTITY zotero.preferences.keys.importFromClipboard "Import from Clipboard">
|
||||
<!ENTITY zotero.preferences.keys.overrideGlobal "Try to override conflicting shortcuts">
|
||||
<!ENTITY zotero.preferences.keys.changesTakeEffect "Changes take effect in new windows only">
|
||||
|
||||
<!ENTITY zotero.preferences.prefpane.proxies "Proxies">
|
||||
|
|
|
@ -934,6 +934,10 @@ locate.manageLocateEngines = Manage Lookup Engines…
|
|||
standalone.corruptInstallation = Your Zotero Standalone installation appears to be corrupted due to a failed auto-update. While Zotero may continue to function, to avoid potential bugs, please download the latest version of Zotero Standalone from http://zotero.org/support/standalone as soon as possible.
|
||||
standalone.addonInstallationFailed.title = Add-on Installation Failed
|
||||
standalone.addonInstallationFailed.body = The add-on "%S" could not be installed. It may be incompatible with this version of Zotero Standalone.
|
||||
standalone.rootWarning = You appear to be running Zotero Standalone as root. This is insecure and may prevent Zotero from functioning when launched from your user account.\n\nIf you wish to install an automatic update, modify the Zotero program directory to be writeable by your user account.
|
||||
standalone.rootWarning.exit = Exit
|
||||
standalone.rootWarning.continue = Continue
|
||||
standalone.updateMessage = A recommended update is available, but you do not have permission to install it. To update automatically, modify the Zotero program directory to be writeable by your user account.
|
||||
|
||||
connector.error.title = Zotero Connector Error
|
||||
connector.standaloneOpen = Your database cannot be accessed because Zotero Standalone is currently open. Please view your items in Zotero Standalone.
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
<!ENTITY preferencesCmdMac.label "Innstillingar">
|
||||
<!ENTITY preferencesCmdMac.label "Innstillingar …">
|
||||
<!ENTITY preferencesCmdMac.commandkey ",">
|
||||
<!ENTITY servicesMenuMac.label "Services">
|
||||
<!ENTITY servicesMenuMac.label "Tenestar">
|
||||
<!ENTITY hideThisAppCmdMac.label "Gøym &brandShortName;">
|
||||
<!ENTITY hideThisAppCmdMac.commandkey "H">
|
||||
<!ENTITY hideThisAppCmdMac.commandkey "G">
|
||||
<!ENTITY hideOtherAppsCmdMac.label "Gøym andre">
|
||||
<!ENTITY hideOtherAppsCmdMac.commandkey "H">
|
||||
<!ENTITY hideOtherAppsCmdMac.commandkey "G">
|
||||
<!ENTITY showAllAppsCmdMac.label "Vis alle">
|
||||
<!ENTITY quitApplicationCmdMac.label "Avslutt Zotero">
|
||||
<!ENTITY quitApplicationCmdMac.key "Q">
|
||||
|
@ -12,52 +12,52 @@
|
|||
|
||||
<!ENTITY fileMenu.label "Fil">
|
||||
<!ENTITY fileMenu.accesskey "F">
|
||||
<!ENTITY saveCmd.label "Save…">
|
||||
<!ENTITY saveCmd.label "Lagra …">
|
||||
<!ENTITY saveCmd.key "S">
|
||||
<!ENTITY saveCmd.accesskey "A">
|
||||
<!ENTITY pageSetupCmd.label "Page Setup…">
|
||||
<!ENTITY pageSetupCmd.accesskey "U">
|
||||
<!ENTITY printCmd.label "Print…">
|
||||
<!ENTITY printCmd.key "P">
|
||||
<!ENTITY printCmd.accesskey "P">
|
||||
<!ENTITY saveCmd.accesskey "L">
|
||||
<!ENTITY pageSetupCmd.label "Sideoppsett …">
|
||||
<!ENTITY pageSetupCmd.accesskey "D">
|
||||
<!ENTITY printCmd.label "Skriv ut …">
|
||||
<!ENTITY printCmd.key "S">
|
||||
<!ENTITY printCmd.accesskey "S">
|
||||
<!ENTITY closeCmd.label "Lukk">
|
||||
<!ENTITY closeCmd.key "W">
|
||||
<!ENTITY closeCmd.accesskey "C">
|
||||
<!ENTITY closeCmd.accesskey "L">
|
||||
<!ENTITY quitApplicationCmdWin.label "Avslutt">
|
||||
<!ENTITY quitApplicationCmdWin.accesskey "x">
|
||||
<!ENTITY quitApplicationCmdWin.accesskey "A">
|
||||
<!ENTITY quitApplicationCmd.label "Avslutt">
|
||||
<!ENTITY quitApplicationCmd.accesskey "Q">
|
||||
<!ENTITY quitApplicationCmd.accesskey "A">
|
||||
|
||||
|
||||
<!ENTITY editMenu.label "Rediger">
|
||||
<!ENTITY editMenu.accesskey "E">
|
||||
<!ENTITY undoCmd.label "Angre">
|
||||
<!ENTITY editMenu.accesskey "R">
|
||||
<!ENTITY undoCmd.label "Angra">
|
||||
<!ENTITY undoCmd.key "Z">
|
||||
<!ENTITY undoCmd.accesskey "U">
|
||||
<!ENTITY undoCmd.accesskey "A">
|
||||
<!ENTITY redoCmd.label "Gjer om">
|
||||
<!ENTITY redoCmd.key "Y">
|
||||
<!ENTITY redoCmd.accesskey "R">
|
||||
<!ENTITY redoCmd.accesskey "G">
|
||||
<!ENTITY cutCmd.label "Klipp ut">
|
||||
<!ENTITY cutCmd.key "X">
|
||||
<!ENTITY cutCmd.accesskey "t">
|
||||
<!ENTITY copyCmd.label "Kopier">
|
||||
<!ENTITY copyCmd.key "C">
|
||||
<!ENTITY copyCmd.accesskey "C">
|
||||
<!ENTITY copyCmd.accesskey "K">
|
||||
<!ENTITY copyCitationCmd.label "Kopier sitering">
|
||||
<!ENTITY copyBibliographyCmd.label "Kopier bibliografi">
|
||||
<!ENTITY pasteCmd.label "Lim inn">
|
||||
<!ENTITY pasteCmd.key "V">
|
||||
<!ENTITY pasteCmd.accesskey "P">
|
||||
<!ENTITY pasteCmd.accesskey "L">
|
||||
<!ENTITY deleteCmd.label "Slett">
|
||||
<!ENTITY deleteCmd.key "D">
|
||||
<!ENTITY deleteCmd.accesskey "D">
|
||||
<!ENTITY deleteCmd.accesskey "S">
|
||||
<!ENTITY selectAllCmd.label "Merk alt">
|
||||
<!ENTITY selectAllCmd.key "A">
|
||||
<!ENTITY selectAllCmd.accesskey "A">
|
||||
<!ENTITY preferencesCmd.label "Preferences">
|
||||
<!ENTITY preferencesCmd.accesskey "O">
|
||||
<!ENTITY preferencesCmdUnix.label "Preferences">
|
||||
<!ENTITY preferencesCmdUnix.accesskey "n">
|
||||
<!ENTITY preferencesCmd.label "Innstillingar …">
|
||||
<!ENTITY preferencesCmd.accesskey "I">
|
||||
<!ENTITY preferencesCmdUnix.label "Innstillingar">
|
||||
<!ENTITY preferencesCmdUnix.accesskey "I">
|
||||
<!ENTITY findCmd.label "Finn">
|
||||
<!ENTITY findCmd.accesskey "F">
|
||||
<!ENTITY findCmd.commandkey "f">
|
||||
|
@ -69,7 +69,7 @@
|
|||
|
||||
|
||||
<!ENTITY toolsMenu.label "Verktøy">
|
||||
<!ENTITY toolsMenu.accesskey "T">
|
||||
<!ENTITY toolsMenu.accesskey "V">
|
||||
<!ENTITY addons.label "Tillegg">
|
||||
|
||||
|
||||
|
@ -88,14 +88,14 @@
|
|||
<!ENTITY helpMenuWin.accesskey "H">
|
||||
<!ENTITY helpMac.commandkey "?">
|
||||
<!ENTITY aboutProduct.label "Om &brandShortName;">
|
||||
<!ENTITY aboutProduct.accesskey "A">
|
||||
<!ENTITY aboutProduct.accesskey "O">
|
||||
<!ENTITY productHelp.label "Hjelp og dokumentasjon">
|
||||
<!ENTITY productHelp.accesskey "D">
|
||||
<!ENTITY productHelp.accesskey "H">
|
||||
<!ENTITY helpTroubleshootingInfo.label "Feilsøking">
|
||||
<!ENTITY helpTroubleshootingInfo.accesskey "T">
|
||||
<!ENTITY helpTroubleshootingInfo.accesskey "F">
|
||||
<!ENTITY helpFeedbackPage.label "Send tilbakemelding …">
|
||||
<!ENTITY helpFeedbackPage.accesskey "S">
|
||||
<!ENTITY helpReportErrors.label "Meld feil i Zotero …">
|
||||
<!ENTITY helpReportErrors.accesskey "R">
|
||||
<!ENTITY helpReportErrors.accesskey "M">
|
||||
<!ENTITY helpCheckForUpdates.label "Sjå etter oppdateringar …">
|
||||
<!ENTITY helpCheckForUpdates.accesskey "U">
|
||||
<!ENTITY helpCheckForUpdates.accesskey "O">
|
||||
|
|
|
@ -20,19 +20,10 @@
|
|||
|
||||
#reindex
|
||||
{
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
padding-left: 5px;
|
||||
list-style-image: url(chrome://zotero/skin/arrow_refresh.png);
|
||||
}
|
||||
|
||||
#reindex .toolbarbutton-icon
|
||||
{
|
||||
margin: 0 0 0 2px;
|
||||
padding: 0;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
}
|
||||
|
||||
#index-box > button
|
||||
{
|
||||
font-size: .95em;
|
||||
|
|
|
@ -65,14 +65,15 @@ pref("extensions.zotero.tagCloud", false);
|
|||
// Keyboard shortcuts
|
||||
pref("extensions.zotero.keys.openZotero", 'Z');
|
||||
pref("extensions.zotero.keys.toggleFullscreen", 'F');
|
||||
pref("extensions.zotero.keys.library", 'L');
|
||||
pref("extensions.zotero.keys.quicksearch", 'K');
|
||||
pref("extensions.zotero.keys.saveToZotero", 'S');
|
||||
pref("extensions.zotero.keys.newItem", 'N');
|
||||
pref("extensions.zotero.keys.newNote", 'O');
|
||||
pref("extensions.zotero.keys.toggleTagSelector", 'T');
|
||||
pref("extensions.zotero.keys.importFromClipboard", 'V');
|
||||
pref("extensions.zotero.keys.library", 'L');
|
||||
pref("extensions.zotero.keys.quicksearch", 'K');
|
||||
pref("extensions.zotero.keys.copySelectedItemCitationsToClipboard", 'A');
|
||||
pref("extensions.zotero.keys.copySelectedItemsToClipboard", 'C');
|
||||
pref("extensions.zotero.keys.importFromClipboard", 'V');
|
||||
pref("extensions.zotero.keys.toggleTagSelector", 'T');
|
||||
|
||||
// Fulltext indexing
|
||||
pref("extensions.zotero.fulltext.textMaxLength", 500000);
|
||||
|
|
|
@ -602,7 +602,9 @@ array_reduce(
|
|||
"dispatch",
|
||||
"when", "spread",
|
||||
"get", "put", "set", "del", "delete",
|
||||
"post", "send", "invoke",
|
||||
// .send() disabled by Zotero for Mozilla Task.jsm compatibility
|
||||
//"post", "send", "invoke",
|
||||
"post", "invoke",
|
||||
"keys",
|
||||
"fapply", "fcall", "fbind",
|
||||
"all", "allResolved",
|
||||
|
@ -1146,7 +1148,8 @@ var post = Q.post = dispatcher("post");
|
|||
* @param ...args array of invocation arguments
|
||||
* @return promise for the return value
|
||||
*/
|
||||
Q.send = send;
|
||||
// Disabled by Zotero for Mozilla Task.jsm compatibility
|
||||
//Q.send = send;
|
||||
Q.invoke = send; // synonyms
|
||||
function send(value, name) {
|
||||
var args = array_slice(arguments, 2);
|
||||
|
|
|
@ -151,6 +151,7 @@
|
|||
"mla-underline": "modern-language-association-underline",
|
||||
"mla-url": "modern-language-association-with-url",
|
||||
"mla": "modern-language-association",
|
||||
"modern-language-association-note": "modern-language-association-6th-edition-note",
|
||||
"molecular-biochemical-parasitology": "molecular-and-biochemical-parasitology",
|
||||
"national-library-of-medicine-grant": "national-library-of-medicine-grant-proposals",
|
||||
"nature-neuroscience-brief-communication": "nature-neuroscience-brief-communications",
|
||||
|
|
|
@ -1 +1 @@
|
|||
2013-07-02 00:00:00
|
||||
2013-07-22 02:55:00
|
||||
|
|
2
styles
2
styles
|
@ -1 +1 @@
|
|||
Subproject commit b5a55500a81e470ec56a10c1757657a050e2c8fe
|
||||
Subproject commit c536a2c5c28ca465b511733a849ae452822fd363
|
|
@ -1 +1 @@
|
|||
Subproject commit 089b8a1ae7d08ffa420b768509f2ae33cce3ab7f
|
||||
Subproject commit 10e7c43ed0e70cdccebf575d4cdccd81b8914ffe
|
Loading…
Reference in New Issue
Block a user