diff --git a/web/download_manager.js b/web/download_manager.js
new file mode 100644
index 000000000..edf5da1af
--- /dev/null
+++ b/web/download_manager.js
@@ -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;
+ }
+ // 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;
+})();
diff --git a/web/firefoxcom.js b/web/firefoxcom.js
index 5a363aa20..c0ef68717 100644
--- a/web/firefoxcom.js
+++ b/web/firefoxcom.js
@@ -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;
+})();
diff --git a/web/ui_utils.js b/web/ui_utils.js
index 005942dc1..a0e777b1d 100644
--- a/web/ui_utils.js
+++ b/web/ui_utils.js
@@ -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() {
diff --git a/web/viewer.html b/web/viewer.html
index 9eba078f0..68b648f67 100644
--- a/web/viewer.html
+++ b/web/viewer.html
@@ -76,6 +76,7 @@ limitations under the License.
+
diff --git a/web/viewer.js b/web/viewer.js
index c2bb36be1..57226e41d 100644
--- a/web/viewer.js
+++ b/web/viewer.js
@@ -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);
- }
- // 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);