Skip to content

Commit 12a4cbb

Browse files
committed
Add MIME type to the generated .epub file
1 parent 03dd484 commit 12a4cbb

File tree

4 files changed

+176
-189
lines changed

4 files changed

+176
-189
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,9 @@ in Chrome:
6666
3. Click on Keyboard shortcuts
6767
```
6868

69+
## Added in 1.4.2
70+
- Added MIME type to the generated .epub file
71+
6972
## Added in 1.4.1
7073
- Remove unnecessary permissions
7174
- Detect image type if the URL doesn't have a file extension (jpg, gif, png)

web-extension/libs/filesaver.js

Lines changed: 170 additions & 187 deletions
Original file line numberDiff line numberDiff line change
@@ -1,188 +1,171 @@
1-
/* FileSaver.js
2-
* A saveAs() FileSaver implementation.
3-
* 1.3.2
4-
* 2016-06-16 18:25:19
5-
*
6-
* By Eli Grey, http://eligrey.com
7-
* License: MIT
8-
* See https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md
9-
*/
10-
11-
/*global self */
12-
/*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
13-
14-
/*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
15-
16-
var saveAs = saveAs || (function(view) {
17-
"use strict";
18-
// IE <10 is explicitly unsupported
19-
if (typeof view === "undefined" || typeof navigator !== "undefined" && /MSIE [1-9]\./.test(navigator.userAgent)) {
20-
return;
21-
}
22-
var
23-
doc = view.document
24-
// only get URL when necessary in case Blob.js hasn't overridden it yet
25-
, get_URL = function() {
26-
return view.URL || view.webkitURL || view;
27-
}
28-
, save_link = doc.createElementNS("http://www.w3.org/1999/xhtml", "a")
29-
, can_use_save_link = "download" in save_link
30-
, click = function(node) {
31-
var event = new MouseEvent("click");
32-
node.dispatchEvent(event);
33-
}
34-
, is_safari = /constructor/i.test(view.HTMLElement)
35-
, is_chrome_ios =/CriOS\/[\d]+/.test(navigator.userAgent)
36-
, throw_outside = function(ex) {
37-
(view.setImmediate || view.setTimeout)(function() {
38-
throw ex;
39-
}, 0);
40-
}
41-
, force_saveable_type = "application/octet-stream"
42-
// the Blob API is fundamentally broken as there is no "downloadfinished" event to subscribe to
43-
, arbitrary_revoke_timeout = 1000 * 40 // in ms
44-
, revoke = function(file) {
45-
var revoker = function() {
46-
if (typeof file === "string") { // file is an object URL
47-
get_URL().revokeObjectURL(file);
48-
} else { // file is a File
49-
file.remove();
50-
}
51-
};
52-
setTimeout(revoker, arbitrary_revoke_timeout);
53-
}
54-
, dispatch = function(filesaver, event_types, event) {
55-
event_types = [].concat(event_types);
56-
var i = event_types.length;
57-
while (i--) {
58-
var listener = filesaver["on" + event_types[i]];
59-
if (typeof listener === "function") {
60-
try {
61-
listener.call(filesaver, event || filesaver);
62-
} catch (ex) {
63-
throw_outside(ex);
64-
}
65-
}
66-
}
67-
}
68-
, auto_bom = function(blob) {
69-
// prepend BOM for UTF-8 XML and text/* types (including HTML)
70-
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
71-
if (/^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
72-
return new Blob([String.fromCharCode(0xFEFF), blob], {type: blob.type});
73-
}
74-
return blob;
75-
}
76-
, FileSaver = function(blob, name, no_auto_bom) {
77-
if (!no_auto_bom) {
78-
blob = auto_bom(blob);
79-
}
80-
// First try a.download, then web filesystem, then object URLs
81-
var
82-
filesaver = this
83-
, type = blob.type
84-
, force = type === force_saveable_type
85-
, object_url
86-
, dispatch_all = function() {
87-
dispatch(filesaver, "writestart progress write writeend".split(" "));
88-
}
89-
// on any filesys errors revert to saving with object URLs
90-
, fs_error = function() {
91-
if ((is_chrome_ios || (force && is_safari)) && view.FileReader) {
92-
// Safari doesn't allow downloading of blob urls
93-
var reader = new FileReader();
94-
reader.onloadend = function() {
95-
var url = is_chrome_ios ? reader.result : reader.result.replace(/^data:[^;]*;/, 'data:attachment/file;');
96-
var popup = view.open(url, '_blank');
97-
if(!popup) view.location.href = url;
98-
url=undefined; // release reference before dispatching
99-
filesaver.readyState = filesaver.DONE;
100-
dispatch_all();
101-
};
102-
reader.readAsDataURL(blob);
103-
filesaver.readyState = filesaver.INIT;
104-
return;
105-
}
106-
// don't create more object URLs than needed
107-
if (!object_url) {
108-
object_url = get_URL().createObjectURL(blob);
109-
}
110-
if (force) {
111-
view.location.href = object_url;
112-
} else {
113-
var opened = view.open(object_url, "_blank");
114-
if (!opened) {
115-
// Apple does not allow window.open, see https://developer.apple.com/library/safari/documentation/Tools/Conceptual/SafariExtensionGuide/WorkingwithWindowsandTabs/WorkingwithWindowsandTabs.html
116-
view.location.href = object_url;
117-
}
118-
}
119-
filesaver.readyState = filesaver.DONE;
120-
dispatch_all();
121-
revoke(object_url);
122-
}
123-
;
124-
filesaver.readyState = filesaver.INIT;
125-
126-
if (can_use_save_link) {
127-
object_url = get_URL().createObjectURL(blob);
128-
setTimeout(function() {
129-
save_link.href = object_url;
130-
save_link.download = name;
131-
click(save_link);
132-
dispatch_all();
133-
revoke(object_url);
134-
filesaver.readyState = filesaver.DONE;
135-
});
136-
return;
137-
}
138-
139-
fs_error();
140-
}
141-
, FS_proto = FileSaver.prototype
142-
, saveAs = function(blob, name, no_auto_bom) {
143-
return new FileSaver(blob, name || blob.name || "download", no_auto_bom);
144-
}
145-
;
146-
// IE 10+ (native saveAs)
147-
if (typeof navigator !== "undefined" && navigator.msSaveOrOpenBlob) {
148-
return function(blob, name, no_auto_bom) {
149-
name = name || blob.name || "download";
150-
151-
if (!no_auto_bom) {
152-
blob = auto_bom(blob);
153-
}
154-
return navigator.msSaveOrOpenBlob(blob, name);
155-
};
156-
}
157-
158-
FS_proto.abort = function(){};
159-
FS_proto.readyState = FS_proto.INIT = 0;
160-
FS_proto.WRITING = 1;
161-
FS_proto.DONE = 2;
162-
163-
FS_proto.error =
164-
FS_proto.onwritestart =
165-
FS_proto.onprogress =
166-
FS_proto.onwrite =
167-
FS_proto.onabort =
168-
FS_proto.onerror =
169-
FS_proto.onwriteend =
170-
null;
171-
172-
return saveAs;
173-
}(
174-
typeof self !== "undefined" && self
175-
|| typeof window !== "undefined" && window
176-
|| this.content
177-
));
178-
// `self` is undefined in Firefox for Android content script context
179-
// while `this` is nsIContentFrameMessageManager
180-
// with an attribute `content` that corresponds to the window
181-
182-
if (typeof module !== "undefined" && module.exports) {
183-
module.exports.saveAs = saveAs;
184-
} else if ((typeof define !== "undefined" && define !== null) && (define.amd !== null)) {
185-
define([], function() {
186-
return saveAs;
187-
});
1+
/*
2+
* FileSaver.js
3+
* A saveAs() FileSaver implementation.
4+
*
5+
* By Eli Grey, http://eligrey.com
6+
*
7+
* License : https://github.com/eligrey/FileSaver.js/blob/master/LICENSE.md (MIT)
8+
* source : http://purl.eligrey.com/github/FileSaver.js
9+
*/
10+
11+
// The one and only way of getting global scope in all environments
12+
// https://stackoverflow.com/q/3277182/1008999
13+
var _global = typeof window === 'object' && window.window === window
14+
? window : typeof self === 'object' && self.self === self
15+
? self : typeof global === 'object' && global.global === global
16+
? global
17+
: this
18+
19+
function bom (blob, opts) {
20+
if (typeof opts === 'undefined') opts = { autoBom: false }
21+
else if (typeof opts !== 'object') {
22+
console.warn('Deprecated: Expected third argument to be a object')
23+
opts = { autoBom: !opts }
24+
}
25+
26+
// prepend BOM for UTF-8 XML and text/* types (including HTML)
27+
// note: your browser will automatically convert UTF-16 U+FEFF to EF BB BF
28+
if (opts.autoBom && /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i.test(blob.type)) {
29+
return new Blob([String.fromCharCode(0xFEFF), blob], { type: blob.type })
30+
}
31+
return blob
32+
}
33+
34+
function download (url, name, opts) {
35+
var xhr = new XMLHttpRequest()
36+
xhr.open('GET', url)
37+
xhr.responseType = 'blob'
38+
xhr.onload = function () {
39+
saveAs(xhr.response, name, opts)
40+
}
41+
xhr.onerror = function () {
42+
console.error('could not download file')
43+
}
44+
xhr.send()
45+
}
46+
47+
function corsEnabled (url) {
48+
var xhr = new XMLHttpRequest()
49+
// use sync to avoid popup blocker
50+
xhr.open('HEAD', url, false)
51+
try {
52+
xhr.send()
53+
} catch (e) {}
54+
return xhr.status >= 200 && xhr.status <= 299
55+
}
56+
57+
// `a.click()` doesn't work for all browsers (#465)
58+
function click (node) {
59+
try {
60+
node.dispatchEvent(new MouseEvent('click'))
61+
} catch (e) {
62+
var evt = document.createEvent('MouseEvents')
63+
evt.initMouseEvent('click', true, true, window, 0, 0, 0, 80,
64+
20, false, false, false, false, 0, null)
65+
node.dispatchEvent(evt)
66+
}
67+
}
68+
69+
// Detect WebView inside a native macOS app by ruling out all browsers
70+
// We just need to check for 'Safari' because all other browsers (besides Firefox) include that too
71+
// https://www.whatismybrowser.com/guides/the-latest-user-agent/macos
72+
var isMacOSWebView = _global.navigator && /Macintosh/.test(navigator.userAgent) && /AppleWebKit/.test(navigator.userAgent) && !/Safari/.test(navigator.userAgent)
73+
74+
var saveAs = _global.saveAs || (
75+
// probably in some web worker
76+
(typeof window !== 'object' || window !== _global)
77+
? function saveAs () { /* noop */ }
78+
79+
// Use download attribute first if possible (#193 Lumia mobile) unless this is a macOS WebView
80+
: ('download' in HTMLAnchorElement.prototype && !isMacOSWebView)
81+
? function saveAs (blob, name, opts) {
82+
var URL = _global.URL || _global.webkitURL
83+
var a = document.createElement('a')
84+
name = name || blob.name || 'download'
85+
86+
a.download = name
87+
a.rel = 'noopener' // tabnabbing
88+
89+
// TODO: detect chrome extensions & packaged apps
90+
// a.target = '_blank'
91+
92+
if (typeof blob === 'string') {
93+
// Support regular links
94+
a.href = blob
95+
if (a.origin !== location.origin) {
96+
corsEnabled(a.href)
97+
? download(blob, name, opts)
98+
: click(a, a.target = '_blank')
99+
} else {
100+
click(a)
101+
}
102+
} else {
103+
// Support blobs
104+
a.href = URL.createObjectURL(blob)
105+
setTimeout(function () { URL.revokeObjectURL(a.href) }, 4E4) // 40s
106+
setTimeout(function () { click(a) }, 0)
107+
}
108+
}
109+
110+
// Use msSaveOrOpenBlob as a second approach
111+
: 'msSaveOrOpenBlob' in navigator
112+
? function saveAs (blob, name, opts) {
113+
name = name || blob.name || 'download'
114+
115+
if (typeof blob === 'string') {
116+
if (corsEnabled(blob)) {
117+
download(blob, name, opts)
118+
} else {
119+
var a = document.createElement('a')
120+
a.href = blob
121+
a.target = '_blank'
122+
setTimeout(function () { click(a) })
123+
}
124+
} else {
125+
navigator.msSaveOrOpenBlob(bom(blob, opts), name)
126+
}
127+
}
128+
129+
// Fallback to using FileReader and a popup
130+
: function saveAs (blob, name, opts, popup) {
131+
// Open a popup immediately do go around popup blocker
132+
// Mostly only available on user interaction and the fileReader is async so...
133+
popup = popup || open('', '_blank')
134+
if (popup) {
135+
popup.document.title =
136+
popup.document.body.innerText = 'downloading...'
137+
}
138+
139+
if (typeof blob === 'string') return download(blob, name, opts)
140+
141+
var force = blob.type === 'application/octet-stream'
142+
var isSafari = /constructor/i.test(_global.HTMLElement) || _global.safari
143+
var isChromeIOS = /CriOS\/[\d]+/.test(navigator.userAgent)
144+
145+
if ((isChromeIOS || (force && isSafari) || isMacOSWebView) && typeof FileReader !== 'undefined') {
146+
// Safari doesn't allow downloading of blob URLs
147+
var reader = new FileReader()
148+
reader.onloadend = function () {
149+
var url = reader.result
150+
url = isChromeIOS ? url : url.replace(/^data:[^;]*;/, 'data:attachment/file;')
151+
if (popup) popup.location.href = url
152+
else location = url
153+
popup = null // reverse-tabnabbing #460
154+
}
155+
reader.readAsDataURL(blob)
156+
} else {
157+
var URL = _global.URL || _global.webkitURL
158+
var url = URL.createObjectURL(blob)
159+
if (popup) popup.location = url
160+
else location.href = url
161+
popup = null // reverse-tabnabbing #460
162+
setTimeout(function () { URL.revokeObjectURL(url) }, 4E4) // 40s
163+
}
164+
}
165+
)
166+
167+
_global.saveAs = saveAs.saveAs = saveAs
168+
169+
if (typeof module !== 'undefined') {
170+
module.exports = saveAs;
188171
}

web-extension/manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"manifest_version": 2,
33
"name": "__MSG_extName__",
4-
"version": "1.4.1",
4+
"version": "1.4.2",
55
"default_locale": "en",
66
"author": "Alex Adam",
77
"homepage_url": "https://github.com/alexadam/save-as-ebook",

web-extension/saveEbook.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,8 @@ function _buildEbook(allPages, fromMenu=false) {
188188

189189

190190
zip.generateAsync({
191-
type: "blob"
191+
type: "blob",
192+
mimeType: "application/epub+zip"
192193
})
193194
.then(function(content) {
194195
console.log("done !");

0 commit comments

Comments
 (0)