1+ /*! FileSaver.js
2+ * A saveAs() FileSaver implementation.
3+ * 2014-01-24
4+ *
5+ * By Eli Grey, http://eligrey.com
6+ * License: X11/MIT
7+ * See LICENSE.md
8+ */
9+
10+ /*global self */
11+ /*jslint bitwise: true, indent: 4, laxbreak: true, laxcomma: true, smarttabs: true, plusplus: true */
12+
13+ /*! @source http://purl.eligrey.com/github/FileSaver.js/blob/master/FileSaver.js */
14+
15+ var saveAs = saveAs
16+ // IE 10+ (native saveAs)
17+ || ( typeof navigator !== "undefined" &&
18+ navigator . msSaveOrOpenBlob && navigator . msSaveOrOpenBlob . bind ( navigator ) )
19+ // Everyone else
20+ || ( function ( view ) {
21+ "use strict" ;
22+ // IE <10 is explicitly unsupported
23+ if ( typeof navigator !== "undefined" &&
24+ / M S I E [ 1 - 9 ] \. / . test ( navigator . userAgent ) ) {
25+ return ;
26+ }
27+ var
28+ doc = view . document
29+ // only get URL when necessary in case BlobBuilder.js hasn't overridden it yet
30+ , get_URL = function ( ) {
31+ return view . URL || view . webkitURL || view ;
32+ }
33+ , URL = view . URL || view . webkitURL || view
34+ , save_link = doc . createElementNS ( "http://www.w3.org/1999/xhtml" , "a" )
35+ , can_use_save_link = ! view . externalHost && "download" in save_link
36+ , click = function ( node ) {
37+ var event = doc . createEvent ( "MouseEvents" ) ;
38+ event . initMouseEvent (
39+ "click" , true , false , view , 0 , 0 , 0 , 0 , 0
40+ , false , false , false , false , 0 , null
41+ ) ;
42+ node . dispatchEvent ( event ) ;
43+ }
44+ , webkit_req_fs = view . webkitRequestFileSystem
45+ , req_fs = view . requestFileSystem || webkit_req_fs || view . mozRequestFileSystem
46+ , throw_outside = function ( ex ) {
47+ ( view . setImmediate || view . setTimeout ) ( function ( ) {
48+ throw ex ;
49+ } , 0 ) ;
50+ }
51+ , force_saveable_type = "application/octet-stream"
52+ , fs_min_size = 0
53+ , deletion_queue = [ ]
54+ , process_deletion_queue = function ( ) {
55+ var i = deletion_queue . length ;
56+ while ( i -- ) {
57+ var file = deletion_queue [ i ] ;
58+ if ( typeof file === "string" ) { // file is an object URL
59+ URL . revokeObjectURL ( file ) ;
60+ } else { // file is a File
61+ file . remove ( ) ;
62+ }
63+ }
64+ deletion_queue . length = 0 ; // clear queue
65+ }
66+ , dispatch = function ( filesaver , event_types , event ) {
67+ event_types = [ ] . concat ( event_types ) ;
68+ var i = event_types . length ;
69+ while ( i -- ) {
70+ var listener = filesaver [ "on" + event_types [ i ] ] ;
71+ if ( typeof listener === "function" ) {
72+ try {
73+ listener . call ( filesaver , event || filesaver ) ;
74+ } catch ( ex ) {
75+ throw_outside ( ex ) ;
76+ }
77+ }
78+ }
79+ }
80+ , FileSaver = function ( blob , name ) {
81+ // First try a.download, then web filesystem, then object URLs
82+ var
83+ filesaver = this
84+ , type = blob . type
85+ , blob_changed = false
86+ , object_url
87+ , target_view
88+ , get_object_url = function ( ) {
89+ var object_url = get_URL ( ) . createObjectURL ( blob ) ;
90+ deletion_queue . push ( object_url ) ;
91+ return object_url ;
92+ }
93+ , dispatch_all = function ( ) {
94+ dispatch ( filesaver , "writestart progress write writeend" . split ( " " ) ) ;
95+ }
96+ // on any filesys errors revert to saving with object URLs
97+ , fs_error = function ( ) {
98+ // don't create more object URLs than needed
99+ if ( blob_changed || ! object_url ) {
100+ object_url = get_object_url ( blob ) ;
101+ }
102+ if ( target_view ) {
103+ target_view . location . href = object_url ;
104+ } else {
105+ window . open ( object_url , "_blank" ) ;
106+ }
107+ filesaver . readyState = filesaver . DONE ;
108+ dispatch_all ( ) ;
109+ }
110+ , abortable = function ( func ) {
111+ return function ( ) {
112+ if ( filesaver . readyState !== filesaver . DONE ) {
113+ return func . apply ( this , arguments ) ;
114+ }
115+ } ;
116+ }
117+ , create_if_not_found = { create : true , exclusive : false }
118+ , slice
119+ ;
120+ filesaver . readyState = filesaver . INIT ;
121+ if ( ! name ) {
122+ name = "download" ;
123+ }
124+ if ( can_use_save_link ) {
125+ object_url = get_object_url ( blob ) ;
126+ // FF for Android has a nasty garbage collection mechanism
127+ // that turns all objects that are not pure javascript into 'deadObject'
128+ // this means `doc` and `save_link` are unusable and need to be recreated
129+ // `view` is usable though:
130+ doc = view . document ;
131+ save_link = doc . createElementNS ( "http://www.w3.org/1999/xhtml" , "a" ) ;
132+ save_link . href = object_url ;
133+ save_link . download = name ;
134+ var event = doc . createEvent ( "MouseEvents" ) ;
135+ event . initMouseEvent (
136+ "click" , true , false , view , 0 , 0 , 0 , 0 , 0
137+ , false , false , false , false , 0 , null
138+ ) ;
139+ save_link . dispatchEvent ( event ) ;
140+ filesaver . readyState = filesaver . DONE ;
141+ dispatch_all ( ) ;
142+ return ;
143+ }
144+ // Object and web filesystem URLs have a problem saving in Google Chrome when
145+ // viewed in a tab, so I force save with application/octet-stream
146+ // http://code.google.com/p/chromium/issues/detail?id=91158
147+ if ( view . chrome && type && type !== force_saveable_type ) {
148+ slice = blob . slice || blob . webkitSlice ;
149+ blob = slice . call ( blob , 0 , blob . size , force_saveable_type ) ;
150+ blob_changed = true ;
151+ }
152+ // Since I can't be sure that the guessed media type will trigger a download
153+ // in WebKit, I append .download to the filename.
154+ // https://bugs.webkit.org/show_bug.cgi?id=65440
155+ if ( webkit_req_fs && name !== "download" ) {
156+ name += ".download" ;
157+ }
158+ if ( type === force_saveable_type || webkit_req_fs ) {
159+ target_view = view ;
160+ }
161+ if ( ! req_fs ) {
162+ fs_error ( ) ;
163+ return ;
164+ }
165+ fs_min_size += blob . size ;
166+ req_fs ( view . TEMPORARY , fs_min_size , abortable ( function ( fs ) {
167+ fs . root . getDirectory ( "saved" , create_if_not_found , abortable ( function ( dir ) {
168+ var save = function ( ) {
169+ dir . getFile ( name , create_if_not_found , abortable ( function ( file ) {
170+ file . createWriter ( abortable ( function ( writer ) {
171+ writer . onwriteend = function ( event ) {
172+ target_view . location . href = file . toURL ( ) ;
173+ deletion_queue . push ( file ) ;
174+ filesaver . readyState = filesaver . DONE ;
175+ dispatch ( filesaver , "writeend" , event ) ;
176+ } ;
177+ writer . onerror = function ( ) {
178+ var error = writer . error ;
179+ if ( error . code !== error . ABORT_ERR ) {
180+ fs_error ( ) ;
181+ }
182+ } ;
183+ "writestart progress write abort" . split ( " " ) . forEach ( function ( event ) {
184+ writer [ "on" + event ] = filesaver [ "on" + event ] ;
185+ } ) ;
186+ writer . write ( blob ) ;
187+ filesaver . abort = function ( ) {
188+ writer . abort ( ) ;
189+ filesaver . readyState = filesaver . DONE ;
190+ } ;
191+ filesaver . readyState = filesaver . WRITING ;
192+ } ) , fs_error ) ;
193+ } ) , fs_error ) ;
194+ } ;
195+ dir . getFile ( name , { create : false } , abortable ( function ( file ) {
196+ // delete file if it already exists
197+ file . remove ( ) ;
198+ save ( ) ;
199+ } ) , abortable ( function ( ex ) {
200+ if ( ex . code === ex . NOT_FOUND_ERR ) {
201+ save ( ) ;
202+ } else {
203+ fs_error ( ) ;
204+ }
205+ } ) ) ;
206+ } ) , fs_error ) ;
207+ } ) , fs_error ) ;
208+ }
209+ , FS_proto = FileSaver . prototype
210+ , saveAs = function ( blob , name ) {
211+ return new FileSaver ( blob , name ) ;
212+ }
213+ ;
214+ FS_proto . abort = function ( ) {
215+ var filesaver = this ;
216+ filesaver . readyState = filesaver . DONE ;
217+ dispatch ( filesaver , "abort" ) ;
218+ } ;
219+ FS_proto . readyState = FS_proto . INIT = 0 ;
220+ FS_proto . WRITING = 1 ;
221+ FS_proto . DONE = 2 ;
222+
223+ FS_proto . error =
224+ FS_proto . onwritestart =
225+ FS_proto . onprogress =
226+ FS_proto . onwrite =
227+ FS_proto . onabort =
228+ FS_proto . onerror =
229+ FS_proto . onwriteend =
230+ null ;
231+
232+ view . addEventListener ( "unload" , process_deletion_queue , false ) ;
233+ saveAs . unload = function ( ) {
234+ process_deletion_queue ( ) ;
235+ view . removeEventListener ( "unload" , process_deletion_queue , false ) ;
236+ } ;
237+ return saveAs ;
238+ } (
239+ typeof self !== "undefined" && self
240+ || typeof window !== "undefined" && window
241+ || this . content
242+ ) ) ;
243+ // `self` is undefined in Firefox for Android content script context
244+ // while `this` is nsIContentFrameMessageManager
245+ // with an attribute `content` that corresponds to the window
246+
247+ if ( typeof module !== "undefined" && module !== null ) {
248+ module . exports = saveAs ;
249+ } else if ( ( typeof define !== "undefined" && define !== null ) && ( define . amd != null ) ) {
250+ define ( [ ] , function ( ) {
251+ return saveAs ;
252+ } ) ;
253+ }
0 commit comments