Refactoring download button logic

This commit is contained in:
Yury Delendik 2013-07-12 13:14:13 -05:00
parent 077f08fa6d
commit ef658bf5f1
5 changed files with 198 additions and 132 deletions

86
web/download_manager.js Normal file
View File

@ -0,0 +1,86 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* Copyright 2013 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals URL*/
'use strict';
var DownloadManager = (function DownloadManagerClosure() {
function download(blobUrl, filename) {
var a = document.createElement('a');
if (a.click) {
// Use a.click() if available. Otherwise, Chrome might show
// "Unsafe JavaScript attempt to initiate a navigation change
// for frame with URL" and not open the PDF at all.
// Supported by (not mentioned = untested):
// - Firefox 6 - 19 (4- does not support a.click, 5 ignores a.click)
// - Chrome 19 - 26 (18- does not support a.click)
// - Opera 9 - 12.15
// - Internet Explorer 6 - 10
// - Safari 6 (5.1- does not support a.click)
a.href = blobUrl;
a.target = '_parent';
// Use a.download if available. This increases the likelihood that
// the file is downloaded instead of opened by another PDF plugin.
if ('download' in a) {
a.download = filename;
}
// <a> must be in the document for IE and recent Firefox versions.
// (otherwise .click() is ignored)
(document.body || document.documentElement).appendChild(a);
a.click();
a.parentNode.removeChild(a);
} else {
if (window.top === window &&
blobUrl.split('#')[0] === window.location.href.split('#')[0]) {
// If _parent == self, then opening an identical URL with different
// location hash will only cause a navigation, not a download.
var padCharacter = blobUrl.indexOf('?') === -1 ? '?' : '&';
blobUrl = blobUrl.replace(/#|$/, padCharacter + '$&');
}
window.open(blobUrl, '_parent');
}
}
function DownloadManager() {}
DownloadManager.prototype = {
downloadUrl: function DownloadManager_downloadUrl(url, filename) {
download(url + '#pdfjs.action=download', filename);
},
download: function DownloadManager_download(blob, url, filename) {
if (!URL) {
// URL.createObjectURL is not supported
this.downloadUrl(url, filename);
return;
}
if (navigator.msSaveBlob) {
// IE10 / IE11
if (!navigator.msSaveBlob(blob, filename)) {
this.downloadUrl(url, filename);
}
return;
}
var blobUrl = URL.createObjectURL(blob);
download(blobUrl, filename);
}
};
return DownloadManager;
})();

View File

@ -1,3 +1,4 @@
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* Copyright 2012 Mozilla Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
@ -13,9 +14,9 @@
* limitations under the License.
*/
var FirefoxCom = (function FirefoxComClosure() {
'use strict';
'use strict';
var FirefoxCom = (function FirefoxComClosure() {
return {
/**
* Creates an event that the extension is listening for and will
@ -69,3 +70,35 @@ var FirefoxCom = (function FirefoxComClosure() {
}
};
})();
var DownloadManager = (function DownloadManagerClosure() {
function DownloadManager() {}
DownloadManager.prototype = {
downloadUrl: function DownloadManager_downloadUrl(url) {
FirefoxCom.request('download', {
originalUrl: url,
filename: this.filename
});
},
download: function DownloadManager_download(blob, url) {
var blobUrl = window.URL.createObjectURL(blob);
FirefoxCom.request('download', {
blobUrl: blobUrl,
originalUrl: url,
filename: this.filename
},
function response(err) {
if (err && this.onerror) {
this.onerror(err);
}
window.URL.revokeObjectURL(blobUrl);
}.bind(this)
);
}
};
return DownloadManager;
})();

View File

@ -91,6 +91,61 @@ function getOutputScale() {
};
}
/**
* Scrolls specified element into view of its parent.
* element {Object} The element to be visible.
* spot {Object} The object with the top property -- offset from the top edge.
*/
function scrollIntoView(element, spot) {
// Assuming offsetParent is available (it's not available when viewer is in
// hidden iframe or object). We have to scroll: if the offsetParent is not set
// producing the error. See also animationStartedClosure.
var parent = element.offsetParent;
var offsetY = element.offsetTop + element.clientTop;
if (!parent) {
console.error('offsetParent is not set -- cannot scroll');
return;
}
while (parent.clientHeight == parent.scrollHeight) {
offsetY += parent.offsetTop;
parent = parent.offsetParent;
if (!parent)
return; // no need to scroll
}
if (spot)
offsetY += spot.top;
parent.scrollTop = offsetY;
}
/**
* Returns the filename or guessed filename from the url (see issue 3455).
* url {String} The original PDF location.
* @return {String} Guessed PDF file name.
*/
function getPDFFileNameFromURL(url) {
var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
// SCHEME HOST 1.PATH 2.QUERY 3.REF
// Pattern to get last matching NAME.pdf
var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
var splitURI = reURI.exec(url);
var suggestedFilename = reFilename.exec(splitURI[1]) ||
reFilename.exec(splitURI[2]) ||
reFilename.exec(splitURI[3]);
if (suggestedFilename) {
suggestedFilename = suggestedFilename[0];
if (suggestedFilename.indexOf('%') != -1) {
// URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
try {
suggestedFilename =
reFilename.exec(decodeURIComponent(suggestedFilename))[0];
} catch(e) { // Possible (extremely rare) errors:
// URIError "Malformed URI", e.g. for "%AA.pdf"
// TypeError "null has no properties", e.g. for "%2F.pdf"
}
}
}
return suggestedFilename || 'document.pdf';
}
var ProgressBar = (function ProgressBarClosure() {

View File

@ -76,6 +76,7 @@ limitations under the License.
<!--#if !PRODUCTION-->
<script type="text/javascript" src="ui_utils.js"></script>
<script type="text/javascript" src="download_manager.js"></script>
<script type="text/javascript" src="text_layer_builder.js"></script>
<script type="text/javascript" src="pdf_find_bar.js"></script>
<script type="text/javascript" src="pdf_find_controller.js"></script>

View File

@ -14,10 +14,9 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/* globals URL*/
/* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar */
/* globals PDFFindController, ProgressBar, getFileName, CustomStyle */
/* globals getOutputScale, TextLayerBuilder */
/* globals PDFJS, PDFBug, FirefoxCom, Stats, Cache, PDFFindBar, CustomStyle,
PDFFindController, ProgressBar, TextLayerBuilder, DownloadManager,
getFileName, getOutputScale, scrollIntoView, getPDFFileNameFromURL */
'use strict';
@ -55,28 +54,9 @@ PDFJS.imageResourcesPath = './images/';
var mozL10n = document.mozL10n || document.webL10n;
//#include ui_utils.js
function scrollIntoView(element, spot) {
// Assuming offsetParent is available (it's not available when viewer is in
// hidden iframe or object). We have to scroll: if the offsetParent is not set
// producing the error. See also animationStartedClosure.
var parent = element.offsetParent;
var offsetY = element.offsetTop + element.clientTop;
if (!parent) {
console.error('offsetParent is not set -- cannot scroll');
return;
}
while (parent.clientHeight == parent.scrollHeight) {
offsetY += parent.offsetTop;
parent = parent.offsetParent;
if (!parent)
return; // no need to scroll
}
if (spot)
offsetY += spot.top;
parent.scrollTop = offsetY;
}
//#if GENERIC || CHROME
//#include download_manager.js
//#endif
//#if FIREFOX || MOZCENTRAL
//#include firefoxcom.js
@ -915,117 +895,28 @@ var PDFView = {
},
download: function pdfViewDownload() {
var url = this.url.split('#')[0];
function getPDFFileNameFromURL(url) {
var reURI = /^(?:([^:]+:)?\/\/[^\/]+)?([^?#]*)(\?[^#]*)?(#.*)?$/;
// SCHEME HOST 1.PATH 2.QUERY 3.REF
// Pattern to get last matching NAME.pdf
var reFilename = /[^\/?#=]+\.pdf\b(?!.*\.pdf\b)/i;
var splitURI = reURI.exec(url);
var suggestedFilename = reFilename.exec(splitURI[1]) ||
reFilename.exec(splitURI[2]) ||
reFilename.exec(splitURI[3]);
if (suggestedFilename) {
suggestedFilename = suggestedFilename[0];
if (suggestedFilename.indexOf('%') != -1) {
// URL-encoded %2Fpath%2Fto%2Ffile.pdf should be file.pdf
try {
suggestedFilename =
reFilename.exec(decodeURIComponent(suggestedFilename))[0];
} catch(e) { // Possible (extremely rare) errors:
// URIError "Malformed URI", e.g. for "%AA.pdf"
// TypeError "null has no properties", e.g. for "%2F.pdf"
}
}
}
return suggestedFilename || 'document.pdf';
}
//#if !(FIREFOX || MOZCENTRAL)
function noData() {
triggerSaveAs(url + '#pdfjs.action=download');
downloadManager.downloadUrl(url, filename);
}
function triggerSaveAs(url, blobUrl) {
// If blobUrl is not specified, fall back to non-blob url.
if (!blobUrl) blobUrl = url;
var a = document.createElement('a');
if (a.click) {
// Use a.click() if available. Otherwise, Chrome might show
// "Unsafe JavaScript attempt to initiate a navigation change
// for frame with URL" and not open the PDF at all.
// Supported by (not mentioned = untested):
// - Firefox 6 - 19 (4- does not support a.click, 5 ignores a.click)
// - Chrome 19 - 26 (18- does not support a.click)
// - Opera 9 - 12.15
// - Internet Explorer 6 - 10
// - Safari 6 (5.1- does not support a.click)
a.href = blobUrl;
a.target = '_parent';
// Use a.download if available. This increases the likelihood that
// the file is downloaded instead of opened by another PDF plugin.
if ('download' in a) {
a.download = getPDFFileNameFromURL(url);
}
// <a> must be in the document for IE and recent Firefox versions.
// (otherwise .click() is ignored)
(document.body || document.documentElement).appendChild(a);
a.click();
a.parentNode.removeChild(a);
} else {
if (window.top === window &&
blobUrl.split('#')[0] === window.location.href.split('#')[0]) {
// If _parent == self, then opening an identical URL with different
// location hash will only cause a navigation, not a download.
var padCharacter = blobUrl.indexOf('?') === -1 ? '?' : '&';
blobUrl = blobUrl.replace(/#|$/, padCharacter + '$&');
}
window.open(blobUrl, '_parent');
}
}
//#else
// function noData() {
// FirefoxCom.request('download', {
// originalUrl: url,
// filename: getPDFFileNameFromURL(url)
// });
// }
// function triggerSaveAs(url, blobUrl) {
// FirefoxCom.request('download', {
// blobUrl: blobUrl,
// originalUrl: url,
// filename: getPDFFileNameFromURL(url)
// },
// function response(err) {
// if (err) {
// // This error won't really be helpful because it's likely the
// // fallback won't work either (or is already open).
// PDFView.error('PDF failed to download.');
// }
// window.URL.revokeObjectURL(blobUrl);
// }
// );
// }
//#endif
// If the PDF is not ready yet, or if URL.createObjectURL is not supported,
// just try to download with the url.
if (!this.pdfDocument || !URL) {
noData();
return;
var url = this.url.split('#')[0];
var filename = getPDFFileNameFromURL(url);
var downloadManager = new DownloadManager();
downloadManager.onerror = function (err) {
// This error won't really be helpful because it's likely the
// fallback won't work either (or is already open).
PDFView.error('PDF failed to download.');
};
if (!this.pdfDocument) { // the PDF is not ready yet
noData();
return;
}
this.pdfDocument.getData().then(
function getDataSuccess(data) {
var blob = PDFJS.createBlob(data.buffer, 'application/pdf');
//#if GENERIC
if (navigator.msSaveBlob) {
// IE10 / IE11
if (!navigator.msSaveBlob(blob, getPDFFileNameFromURL(url))) {
noData();
}
return;
}
//#endif
var blobUrl = URL.createObjectURL(blob);
triggerSaveAs(url, blobUrl);
downloadManager.download(blob, url, filename);
},
noData // Error occurred try downloading with just the url.
).then(null, noData);