1
+ //download.js v4.2, by dandavis; 2008-2016. [CCBY2] see http://danml.com/download.html for tests/usage
2
+ // v1 landed a FF+Chrome compat way of downloading strings to local un-named files, upgraded to use a hidden frame and optional mime
3
+ // v2 added named files via a[download], msSaveBlob, IE (10+) support, and window.URL support for larger+faster saves than dataURLs
4
+ // v3 added dataURL and Blob Input, bind-toggle arity, and legacy dataURL fallback was improved with force-download mime and base64 support. 3.1 improved safari handling.
5
+ // v4 adds AMD/UMD, commonJS, and plain browser support
6
+ // v4.1 adds url download capability via solo URL argument (same domain/CORS only)
7
+ // v4.2 adds semantic variable names, long (over 2MB) dataURL support, and hidden by default temp anchors
8
+ // https://github.com/rndme/download
9
+
10
+ ( function ( root , factory ) {
11
+ if ( typeof define === 'function' && define . amd ) {
12
+ // AMD. Register as an anonymous module.
13
+ define ( [ ] , factory ) ;
14
+ } else if ( typeof exports === 'object' ) {
15
+ // Node. Does not work with strict CommonJS, but
16
+ // only CommonJS-like environments that support module.exports,
17
+ // like Node.
18
+ module . exports = factory ( ) ;
19
+ } else {
20
+ // Browser globals (root is window)
21
+ root . download = factory ( ) ;
22
+ }
23
+ } ( this , function ( ) {
24
+
25
+ return function download ( data , strFileName , strMimeType ) {
26
+
27
+ var self = window , // this script is only for browsers anyway...
28
+ defaultMime = "application/octet-stream" , // this default mime also triggers iframe downloads
29
+ mimeType = strMimeType || defaultMime ,
30
+ payload = data ,
31
+ url = ! strFileName && ! strMimeType && payload ,
32
+ anchor = document . createElement ( "a" ) ,
33
+ toString = function ( a ) { return String ( a ) ; } ,
34
+ myBlob = ( self . Blob || self . MozBlob || self . WebKitBlob || toString ) ,
35
+ fileName = strFileName || "download" ,
36
+ blob ,
37
+ reader ;
38
+ myBlob = myBlob . call ? myBlob . bind ( self ) : Blob ;
39
+
40
+ if ( String ( this ) === "true" ) { //reverse arguments, allowing download.bind(true, "text/xml", "export.xml") to act as a callback
41
+ payload = [ payload , mimeType ] ;
42
+ mimeType = payload [ 0 ] ;
43
+ payload = payload [ 1 ] ;
44
+ }
45
+
46
+
47
+ if ( url && url . length < 2048 ) { // if no filename and no mime, assume a url was passed as the only argument
48
+ fileName = url . split ( "/" ) . pop ( ) . split ( "?" ) [ 0 ] ;
49
+ anchor . href = url ; // assign href prop to temp anchor
50
+ if ( anchor . href . indexOf ( url ) !== - 1 ) { // if the browser determines that it's a potentially valid url path:
51
+ var ajax = new XMLHttpRequest ( ) ;
52
+ ajax . open ( "GET" , url , true ) ;
53
+ ajax . responseType = 'blob' ;
54
+ ajax . onload = function ( e ) {
55
+ download ( e . target . response , fileName , defaultMime ) ;
56
+ } ;
57
+ setTimeout ( function ( ) { ajax . send ( ) ; } , 0 ) ; // allows setting custom ajax headers using the return:
58
+ return ajax ;
59
+ } // end if valid url?
60
+ } // end if url?
61
+
62
+
63
+ //go ahead and download dataURLs right away
64
+ if ( / ^ d a t a \: [ \w + \- ] + \/ [ \w + \- ] + [ , ; ] / . test ( payload ) ) {
65
+
66
+ if ( payload . length > ( 1024 * 1024 * 1.999 ) && myBlob !== toString ) {
67
+ payload = dataUrlToBlob ( payload ) ;
68
+ mimeType = payload . type || defaultMime ;
69
+ } else {
70
+ return navigator . msSaveBlob ? // IE10 can't do a[download], only Blobs:
71
+ navigator . msSaveBlob ( dataUrlToBlob ( payload ) , fileName ) :
72
+ saver ( payload ) ; // everyone else can save dataURLs un-processed
73
+ }
74
+
75
+ } //end if dataURL passed?
76
+
77
+ blob = payload instanceof myBlob ?
78
+ payload :
79
+ new myBlob ( [ payload ] , { type : mimeType } ) ;
80
+
81
+
82
+ function dataUrlToBlob ( strUrl ) {
83
+ var parts = strUrl . split ( / [: ; , ] / ) ,
84
+ type = parts [ 1 ] ,
85
+ decoder = parts [ 2 ] == "base64" ? atob : decodeURIComponent ,
86
+ binData = decoder ( parts . pop ( ) ) ,
87
+ mx = binData . length ,
88
+ i = 0 ,
89
+ uiArr = new Uint8Array ( mx ) ;
90
+
91
+ for ( i ; i < mx ; ++ i ) uiArr [ i ] = binData . charCodeAt ( i ) ;
92
+
93
+ return new myBlob ( [ uiArr ] , { type : type } ) ;
94
+ }
95
+
96
+ function saver ( url , winMode ) {
97
+
98
+ if ( 'download' in anchor ) { //html5 A[download]
99
+ anchor . href = url ;
100
+ anchor . setAttribute ( "download" , fileName ) ;
101
+ anchor . className = "download-js-link" ;
102
+ anchor . innerHTML = "downloading..." ;
103
+ anchor . style . display = "none" ;
104
+ document . body . appendChild ( anchor ) ;
105
+ setTimeout ( function ( ) {
106
+ anchor . click ( ) ;
107
+ document . body . removeChild ( anchor ) ;
108
+ if ( winMode === true ) { setTimeout ( function ( ) { self . URL . revokeObjectURL ( anchor . href ) ; } , 250 ) ; }
109
+ } , 66 ) ;
110
+ return true ;
111
+ }
112
+
113
+ // handle non-a[download] safari as best we can:
114
+ if ( / ( V e r s i o n ) \/ ( \d + ) \. ( \d + ) (?: \. ( \d + ) ) ? .* S a f a r i \/ / . test ( navigator . userAgent ) ) {
115
+ url = url . replace ( / ^ d a t a : ( [ \w \/ \- \+ ] + ) / , defaultMime ) ;
116
+ if ( ! window . open ( url ) ) { // popup blocked, offer direct download:
117
+ if ( confirm ( "Displaying New Document\n\nUse Save As... to download, then click back to return to this page." ) ) { location . href = url ; }
118
+ }
119
+ return true ;
120
+ }
121
+
122
+ //do iframe dataURL download (old ch+FF):
123
+ var f = document . createElement ( "iframe" ) ;
124
+ document . body . appendChild ( f ) ;
125
+
126
+ if ( ! winMode ) { // force a mime that will download:
127
+ url = "data:" + url . replace ( / ^ d a t a : ( [ \w \/ \- \+ ] + ) / , defaultMime ) ;
128
+ }
129
+ f . src = url ;
130
+ setTimeout ( function ( ) { document . body . removeChild ( f ) ; } , 333 ) ;
131
+
132
+ } //end saver
133
+
134
+
135
+
136
+
137
+ if ( navigator . msSaveBlob ) { // IE10+ : (has Blob, but not a[download] or URL)
138
+ return navigator . msSaveBlob ( blob , fileName ) ;
139
+ }
140
+
141
+ if ( self . URL ) { // simple fast and modern way using Blob and URL:
142
+ saver ( self . URL . createObjectURL ( blob ) , true ) ;
143
+ } else {
144
+ // handle non-Blob()+non-URL browsers:
145
+ if ( typeof blob === "string" || blob . constructor === toString ) {
146
+ try {
147
+ return saver ( "data:" + mimeType + ";base64," + self . btoa ( blob ) ) ;
148
+ } catch ( y ) {
149
+ return saver ( "data:" + mimeType + "," + encodeURIComponent ( blob ) ) ;
150
+ }
151
+ }
152
+
153
+ // Blob but not URL support:
154
+ reader = new FileReader ( ) ;
155
+ reader . onload = function ( e ) {
156
+ saver ( this . result ) ;
157
+ } ;
158
+ reader . readAsDataURL ( blob ) ;
159
+ }
160
+ return true ;
161
+ } ; /* end download() */
162
+ } ) ) ;
0 commit comments