Closes #416, Right-click to add attachment
Added "Save Link As Zotero Snapshot" and "Save Image As Zotero Snapshot" options to the browser content context menu where appropriate Other fixes: - Implemented standalone image and plugin snapshots the right way (as opposed to the fairly broken way from yesterday) - Only natively handled files are loaded into a hidden browser when using importFromURL() -- plugin files are now saved directly with saveURI() - indexDocument() doesn't try to index non-text files Notes: - There's no feedback when saving large files, which will likely be a bit confusing for users -- one option would be to put the transfer into the downloads window, though that's a little weird. - I suspect this will fix the reported JSTOR PDF download issue (http://forums.zotero.org/discussion/217/), though I don't currently have a way of testing it.
This commit is contained in:
parent
507efb4758
commit
0d145cd47b
|
@ -874,10 +874,35 @@ var ZoteroPane = new function()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var menuitem = document.getElementById("zotero-context-save-link-as-snapshot");
|
||||||
|
if (menuitem) {
|
||||||
|
if (window.gContextMenu.onLink) {
|
||||||
|
menuitem.hidden = false;
|
||||||
|
showing = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
menuitem.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var menuitem = document.getElementById("zotero-context-save-image-as-snapshot");
|
||||||
|
if (menuitem) {
|
||||||
|
// Not using window.gContextMenu.hasBGImage -- if the user wants it,
|
||||||
|
// they can use the Firefox option to view and then import from there
|
||||||
|
if (window.gContextMenu.onImage) {
|
||||||
|
menuitem.hidden = false;
|
||||||
|
showing = true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
menuitem.hidden = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var separator = document.getElementById("zotero-context-separator");
|
var separator = document.getElementById("zotero-context-separator");
|
||||||
separator.hidden = !showing;
|
separator.hidden = !showing;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
function newNote(popup, parent, text)
|
function newNote(popup, parent, text)
|
||||||
{
|
{
|
||||||
if (!popup)
|
if (!popup)
|
||||||
|
|
|
@ -52,6 +52,12 @@
|
||||||
<menuitem id="zotero-context-add-to-new-note" class="menu-iconic"
|
<menuitem id="zotero-context-add-to-new-note" class="menu-iconic"
|
||||||
label="&zotero.contextMenu.addTextToNewNote;"
|
label="&zotero.contextMenu.addTextToNewNote;"
|
||||||
oncommand="ZoteroPane.newNote(false, false, window.content.getSelection().toString())"/>
|
oncommand="ZoteroPane.newNote(false, false, window.content.getSelection().toString())"/>
|
||||||
|
<menuitem id="zotero-context-save-link-as-snapshot" class="menu-iconic"
|
||||||
|
label="&zotero.contextMenu.saveLinkAsSnapshot;"
|
||||||
|
oncommand="Zotero.Attachments.importFromURL(window.gContextMenu.linkURL)"/>
|
||||||
|
<menuitem id="zotero-context-save-image-as-snapshot" class="menu-iconic"
|
||||||
|
label="&zotero.contextMenu.saveImageAsSnapshot;"
|
||||||
|
oncommand="Zotero.Attachments.importFromURL(window.gContextMenu.onImage ? window.gContextMenu.imageURL : window.gContextMenu.bgImageURL)"/>
|
||||||
</popup>
|
</popup>
|
||||||
|
|
||||||
<vbox id="appcontent">
|
<vbox id="appcontent">
|
||||||
|
|
|
@ -178,9 +178,9 @@ Zotero.Attachments = new function(){
|
||||||
nsIURL.spec = url;
|
nsIURL.spec = url;
|
||||||
var ext = nsIURL.fileExtension;
|
var ext = nsIURL.fileExtension;
|
||||||
|
|
||||||
// If we can load this internally, use a hidden browser (so we can
|
// If we can load this natively, use a hidden browser (so we can
|
||||||
// get the charset and title)
|
// get the charset and title and index the document)
|
||||||
if (Zotero.MIME.hasInternalHandler(mimeType, ext)){
|
if (Zotero.MIME.hasNativeHandler(mimeType, ext)){
|
||||||
var browser = Zotero.Browser.createHiddenBrowser();
|
var browser = Zotero.Browser.createHiddenBrowser();
|
||||||
browser.addEventListener("pageshow", function(){
|
browser.addEventListener("pageshow", function(){
|
||||||
Zotero.Attachments.importFromDocument(browser.contentDocument, sourceItemID);
|
Zotero.Attachments.importFromDocument(browser.contentDocument, sourceItemID);
|
||||||
|
@ -340,15 +340,17 @@ Zotero.Attachments = new function(){
|
||||||
var title = forceTitle ? forceTitle : document.title;
|
var title = forceTitle ? forceTitle : document.title;
|
||||||
var mimeType = document.contentType;
|
var mimeType = document.contentType;
|
||||||
var charsetID = Zotero.CharacterSets.getID(document.characterSet);
|
var charsetID = Zotero.CharacterSets.getID(document.characterSet);
|
||||||
var hasNativeHandler = Zotero.MIME.hasNativeHandler(mimeType, _getExtensionFromURL(url))
|
|
||||||
|
|
||||||
// TODO: make this work -- with local plugin files, onStateChange in the
|
if (!forceTitle) {
|
||||||
// nsIWebBrowserPersist's nsIWebProgressListener never completes and
|
// Remove e.g. " - Scaled (-17%)" from end of images saved from links,
|
||||||
// onProgressChange returns -1 for maxTotal, which prevents it from
|
// though I'm not sure why it's getting added to begin with
|
||||||
// triggering the callback.
|
if (mimeType.indexOf('image/') === 0) {
|
||||||
if (!hasNativeHandler && url.substr(0, 4) == 'file') {
|
title = title.replace(/(.+ \([^,]+, [0-9]+x[0-9]+[^\)]+\)) - .+/, "$1" );
|
||||||
Zotero.debug('Import of loaded files from plugins is not supported');
|
}
|
||||||
return false;
|
// If not native type, strip mime type data in parens
|
||||||
|
else if (!Zotero.MIME.hasNativeHandler(mimeType, _getExtensionFromURL(url))) {
|
||||||
|
title = title.replace(/(.+) \([a-z]+\/[^\)]+\)/, "$1" );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
|
const nsIWBP = Components.interfaces.nsIWebBrowserPersist;
|
||||||
|
@ -434,13 +436,23 @@ Zotero.Attachments = new function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
Zotero.Fulltext.indexDocument(document, itemID);
|
Zotero.Fulltext.indexDocument(document, itemID);
|
||||||
}, !hasNativeHandler);
|
});
|
||||||
|
|
||||||
// The attachment is still incomplete here, but we can't risk
|
// The attachment is still incomplete here, but we can't risk
|
||||||
// leaving the transaction open if the callback never triggers
|
// leaving the transaction open if the callback never triggers
|
||||||
Zotero.DB.commitTransaction();
|
Zotero.DB.commitTransaction();
|
||||||
|
|
||||||
wbp.saveDocument(document, file, destDir, mimeType, encodingFlags, false);
|
if (Zotero.MIME.isDocumentType(mimeType)) {
|
||||||
|
Zotero.debug('Saving with saveDocument()');
|
||||||
|
wbp.saveDocument(document, file, destDir, mimeType, encodingFlags, false);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Zotero.debug('Saving with saveURI()');
|
||||||
|
var ioService = Components.classes["@mozilla.org/network/io-service;1"]
|
||||||
|
.getService(Components.interfaces.nsIIOService);
|
||||||
|
var nsIURL = ioService.newURI(url, null, null);
|
||||||
|
wbp.saveURI(nsIURL, null, null, null, null, file);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
catch (e) {
|
catch (e) {
|
||||||
Zotero.DB.rollbackTransaction();
|
Zotero.DB.rollbackTransaction();
|
||||||
|
|
|
@ -159,6 +159,16 @@ Zotero.Fulltext = new function(){
|
||||||
|
|
||||||
Zotero.debug("Indexing document '" + document.title + "'");
|
Zotero.debug("Indexing document '" + document.title + "'");
|
||||||
|
|
||||||
|
if (document.contentType.indexOf('text/') !== 0) {
|
||||||
|
Zotero.debug('File is not text in indexDocument()', 2);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!document.characterSet){
|
||||||
|
Zotero.debug("Text file didn't have charset in indexFile()", 1);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var text = document.body.innerHTML.replace(/(>)/g, '$1 ');
|
var text = document.body.innerHTML.replace(/(>)/g, '$1 ');
|
||||||
text = HTMLToText(text);
|
text = HTMLToText(text);
|
||||||
indexString(text, document.characterSet, itemID);
|
indexString(text, document.characterSet, itemID);
|
||||||
|
|
|
@ -26,6 +26,7 @@ Zotero.MIME = new function(){
|
||||||
this.sniffForBinary = sniffForBinary;
|
this.sniffForBinary = sniffForBinary;
|
||||||
this.getMIMETypeFromData = getMIMETypeFromData;
|
this.getMIMETypeFromData = getMIMETypeFromData;
|
||||||
this.getMIMETypeFromFile = getMIMETypeFromFile;
|
this.getMIMETypeFromFile = getMIMETypeFromFile;
|
||||||
|
this.isDocumentType = isDocumentType;
|
||||||
this.hasNativeHandler = hasNativeHandler;
|
this.hasNativeHandler = hasNativeHandler;
|
||||||
this.hasInternalHandler = hasInternalHandler;
|
this.hasInternalHandler = hasInternalHandler;
|
||||||
this.fileHasInternalHandler = fileHasInternalHandler;
|
this.fileHasInternalHandler = fileHasInternalHandler;
|
||||||
|
@ -41,6 +42,14 @@ Zotero.MIME = new function(){
|
||||||
["<?xml", 'text/xml']
|
["<?xml", 'text/xml']
|
||||||
];
|
];
|
||||||
|
|
||||||
|
// MIME types to be saved as documents
|
||||||
|
var _documentMIMETypes = {
|
||||||
|
'text/html': true,
|
||||||
|
'application/xhtml+xml': true,
|
||||||
|
'text/xml': true,
|
||||||
|
'application/xml': true
|
||||||
|
};
|
||||||
|
|
||||||
// MIME types handled natively by Gecko
|
// MIME types handled natively by Gecko
|
||||||
// DEBUG: There's definitely a better way of getting these
|
// DEBUG: There's definitely a better way of getting these
|
||||||
var _nativeMIMETypes = {
|
var _nativeMIMETypes = {
|
||||||
|
@ -137,6 +146,11 @@ Zotero.MIME = new function(){
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function isDocumentType(mimeType) {
|
||||||
|
return _documentMIMETypes[mimeType] ? true : false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Determine if a MIME type can be handled natively
|
* Determine if a MIME type can be handled natively
|
||||||
* or if it needs to be passed off to a plugin or external helper app
|
* or if it needs to be passed off to a plugin or external helper app
|
||||||
|
|
|
@ -1150,44 +1150,18 @@ Zotero.Browser = new function() {
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Implements nsIWebProgressListener
|
* Implements nsIWebProgressListener
|
||||||
*
|
|
||||||
* For plugin content, onStateChange doesn't seem to be called after the document
|
|
||||||
* finishes loading, so the useProgress flag can be used to run onFinish()
|
|
||||||
* when all the content of the request has been loaded -- this should only be
|
|
||||||
* used for single file requests (generally, things handled by plugins)
|
|
||||||
*/
|
*/
|
||||||
Zotero.WebProgressFinishListener = function(onFinish, useProgress, wbp) {
|
Zotero.WebProgressFinishListener = function(onFinish) {
|
||||||
var _finished = false;
|
|
||||||
|
|
||||||
this.onStateChange = function(wp, req, stateFlags, status) {
|
this.onStateChange = function(wp, req, stateFlags, status) {
|
||||||
|
//Zotero.debug('onStageChange: ' + stateFlags);
|
||||||
if ((stateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP)
|
if ((stateFlags & Components.interfaces.nsIWebProgressListener.STATE_STOP)
|
||||||
&& (stateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_NETWORK)) {
|
&& (stateFlags & Components.interfaces.nsIWebProgressListener.STATE_IS_NETWORK)) {
|
||||||
if (useProgress) {
|
|
||||||
Zotero.debug('WebProgressFinishListener: useProgress set but STOP_STOP and STATE_IS_NETWORK were reached', 2);
|
|
||||||
if (_finished) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
_finished = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
onFinish();
|
onFinish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.onLocationChange = function() {}
|
this.onLocationChange = function() {}
|
||||||
|
this.onProgressChange = function() {}
|
||||||
this.onProgressChange = function(wp, req, cur, max, curTotal, maxTotal) {
|
|
||||||
// DEBUG: This will never complete if the file size (maxTotal) isn't
|
|
||||||
// available, which seems to be the case with local files and is
|
|
||||||
// presumably the case with remote servers that don't send the file size.
|
|
||||||
//Zotero.debug('Current total: ' + curTotal + ' Max total: ' + maxTotal);
|
|
||||||
if (!_finished && useProgress && (curTotal == maxTotal)) {
|
|
||||||
_finished = true;
|
|
||||||
onFinish();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.onSecurityChange = function() {}
|
this.onSecurityChange = function() {}
|
||||||
this.onStatusChange = function(wp, req, st, msg) {}
|
this.onStatusChange = function() {}
|
||||||
}
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
<!ENTITY zotero.contextMenu.addTextToCurrentNote "Add Selection to Zotero Note">
|
<!ENTITY zotero.contextMenu.addTextToCurrentNote "Add Selection to Zotero Note">
|
||||||
<!ENTITY zotero.contextMenu.addTextToNewNote "Create Zotero Note from Selection">
|
<!ENTITY zotero.contextMenu.addTextToNewNote "Create Zotero Note from Selection">
|
||||||
|
<!ENTITY zotero.contextMenu.saveLinkAsSnapshot "Save Link As Zotero Snapshot">
|
||||||
|
<!ENTITY zotero.contextMenu.saveImageAsSnapshot "Save Image As Zotero Snapshot">
|
||||||
|
|
||||||
<!ENTITY zotero.tabs.info.label "Info">
|
<!ENTITY zotero.tabs.info.label "Info">
|
||||||
<!ENTITY zotero.tabs.notes.label "Notes">
|
<!ENTITY zotero.tabs.notes.label "Notes">
|
||||||
|
|
Loading…
Reference in New Issue
Block a user