Mercurial Hosting > linkmystyle
comparison src/uploadcare/uploadcare.js @ 0:8f4df159f06b
start public repo
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 11 Jul 2025 20:57:49 -0600 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:8f4df159f06b |
---|---|
1 'use strict'; | |
2 | |
3 let uploadcare = {}; | |
4 | |
5 /* | |
6 | |
7 to set an option: | |
8 uploadcare[option] = value | |
9 | |
10 from https://uploadcare.com/docs/uploads/file-uploader-options/ | |
11 publicKey, imagesOnly, doNotStore | |
12 | |
13 onError - function called with request for AJAX errors | |
14 maxFileSize - maximum file size of images in bytes | |
15 | |
16 */ | |
17 | |
18 uploadcare.maxDim = 2000; | |
19 uploadcare.processingImage = '/uploadcare/processing.gif'; | |
20 uploadcare.cropprOptions = null; | |
21 | |
22 function logToServer(msg) { | |
23 ajax( '/log_info.js', 'msg='+encodeURIComponent(msg) ); | |
24 } | |
25 | |
26 { | |
27 // compression | |
28 | |
29 function infoAddUrl(info) { | |
30 if( info.url ) | |
31 return; | |
32 return new Promise( function(resolve) { | |
33 let reader = new FileReader(); | |
34 reader.onload = function() { | |
35 info.url = reader.result; | |
36 resolve(); | |
37 }; | |
38 reader.readAsDataURL(info.file); | |
39 } ); | |
40 } | |
41 uploadcare.infoAddUrl = infoAddUrl; | |
42 | |
43 let croppr; | |
44 let dialog = null; | |
45 | |
46 async function infoAddCroppedImage(info) { | |
47 if( !dialog ) { | |
48 let html = ` | |
49 <dialog croppr> | |
50 <img> | |
51 <div buttons> | |
52 <button save>Save</button> | |
53 <button cancel>Cancel</button> | |
54 </div> | |
55 </dialog> | |
56 ` ; | |
57 document.body.insertAdjacentHTML( 'beforeend', html ); | |
58 dialog = document.querySelector('dialog[croppr]'); | |
59 dialog.onclose = function() { | |
60 croppr.destroy(); | |
61 }; | |
62 } | |
63 await infoAddUrl(info); | |
64 let image = dialog.querySelector('img'); | |
65 image.src = info.url; | |
66 return new Promise( function(resolve) { | |
67 dialog.querySelector('button[save]').onclick = function() { | |
68 info.image = image; | |
69 info.crop = croppr.getValue(); | |
70 dialog.close(); | |
71 resolve(); | |
72 }; | |
73 dialog.querySelector('button[cancel]').onclick = function() { | |
74 info.canceled = true; | |
75 dialog.close(); | |
76 resolve(); | |
77 }; | |
78 image.onload = function() { | |
79 dialog.showModal(); | |
80 croppr = new Croppr( image, uploadcare.cropprOptions ); | |
81 }; | |
82 } ); | |
83 } | |
84 | |
85 let supportsDialog = typeof HTMLDialogElement === 'function'; | |
86 | |
87 async function infoAddImage(info) { | |
88 if( info.image ) | |
89 return; | |
90 if( uploadcare.cropprOptions && supportsDialog ) | |
91 return infoAddCroppedImage(info); | |
92 await infoAddUrl(info); | |
93 return new Promise( function(resolve) { | |
94 let image = new Image(); | |
95 info.image = image; | |
96 image.src = info.url; | |
97 image.onload = function() { | |
98 resolve(); | |
99 }; | |
100 } ); | |
101 } | |
102 | |
103 async function infoAddCanvas(info,maxDim) { | |
104 await infoAddImage(info); | |
105 if( info.canceled ) | |
106 return; | |
107 let image = info.image; | |
108 let width, height; | |
109 let crop = info.crop; | |
110 if( crop ) { | |
111 width = crop.width; | |
112 height = crop.height; | |
113 } else { | |
114 width = image.width; | |
115 height = image.height; | |
116 } | |
117 if( maxDim ) { | |
118 if( width > height ) { | |
119 if( width > maxDim ) { | |
120 height *= maxDim / width; | |
121 width = maxDim; | |
122 } | |
123 } else { | |
124 if( height > maxDim ) { | |
125 width *= maxDim / height; | |
126 height = maxDim; | |
127 } | |
128 } | |
129 } | |
130 let canvas = document.createElement('canvas'); | |
131 canvas.width = width; | |
132 canvas.height = height; | |
133 let ctx = canvas.getContext('2d'); | |
134 if( crop ) { | |
135 ctx.drawImage( image, crop.x, crop.y, crop.width, crop.height, 0, 0, width, height ); | |
136 } else { | |
137 ctx.drawImage( image, 0, 0, width, height ); | |
138 } | |
139 info.canvas = canvas; | |
140 } | |
141 | |
142 async function infoAddBlob(info,maxDim) { | |
143 await infoAddCanvas(info,maxDim); | |
144 if( info.canceled ) | |
145 return; | |
146 return new Promise( function(resolve) { | |
147 function done(blob) { | |
148 info.blob = blob; | |
149 resolve(); | |
150 } | |
151 info.canvas.toBlob( done, 'image/jpeg' ); | |
152 } ); | |
153 } | |
154 | |
155 async function infoCompress(info) { | |
156 let file = info.file; | |
157 await infoAddBlob(info); | |
158 if( info.canceled ) | |
159 return; | |
160 let maxFileSize = uploadcare.maxFileSize; | |
161 if( !info.blob || maxFileSize && info.blob.size > maxFileSize ) { | |
162 await infoAddBlob(info,uploadcare.maxDim); | |
163 if( !info.blob ) throw 'no blob'; | |
164 } | |
165 info.compressed = info.blob; | |
166 info.compressedName = file.name.replace( /(\.[^.]*)?$/, '.jpeg' ); | |
167 } | |
168 uploadcare.infoCompress = infoCompress; | |
169 | |
170 | |
171 // uploading | |
172 | |
173 function onload(url,onSuccess,onError,count) { | |
174 count = count || 1; | |
175 let request = new XMLHttpRequest(); | |
176 request.open( 'GET', url ); | |
177 request.onload = function() { | |
178 if( request.status === 200 ) { | |
179 onSuccess(); | |
180 } else if( request.status === 404 ) { | |
181 console.log('onload failed '+count); | |
182 if( count >= 20 ) { | |
183 let text = 'Failed to get image after ' + count + ' tries, please try again'; | |
184 onError( request.status, text ); | |
185 return; | |
186 } | |
187 setTimeout( function() { | |
188 onload(url,onSuccess,onError,count+1); | |
189 }, 1000 ); | |
190 } else { | |
191 onError( request.status, request.responseText) ; | |
192 } | |
193 }; | |
194 request.send(); | |
195 } | |
196 | |
197 function call(file,filename,callback,onError) { | |
198 let request = new XMLHttpRequest(); | |
199 let url = 'https://upload.uploadcare.com/base/'; | |
200 request.open( 'POST', url ); | |
201 request.onload = function() { | |
202 if( request.status !== 200 ) { | |
203 onError( request.status, request.responseText ); | |
204 return; | |
205 } | |
206 let response = JSON.parse(request.responseText); | |
207 let uuid = response.file; | |
208 let url = 'https://ucarecdn.com/' + uuid + '/'; | |
209 function onSuccess() { | |
210 callback( uuid, file.name ); | |
211 } | |
212 onload( url, onSuccess, onError ); | |
213 }; | |
214 let formData = new FormData(); | |
215 formData.append( 'UPLOADCARE_PUB_KEY', uploadcare.publicKey ); | |
216 formData.append( 'UPLOADCARE_STORE', uploadcare.doNotStore ? '0' : '1' ); | |
217 formData.append( 'file', file, filename ); | |
218 request.send(formData); | |
219 } | |
220 | |
221 let input = null; | |
222 let img = null; | |
223 | |
224 function showImg() { | |
225 img.style.display = 'block'; | |
226 } | |
227 | |
228 function hideImg() { | |
229 img.style.display = 'none'; | |
230 } | |
231 | |
232 uploadcare.upload = function(callback) { | |
233 if( !uploadcare.publicKey ) | |
234 throw new Error('uploadcare.publicKey required'); | |
235 if( !input ) { | |
236 let html = ` | |
237 <input uploadcare type=file> | |
238 <img uploadcare src="${uploadcare.processingImage}"> | |
239 ` ; | |
240 document.body.insertAdjacentHTML( 'beforeend', html ); | |
241 input = document.querySelector('input[uploadcare][type="file"]'); | |
242 img = document.querySelector('img[uploadcare]'); | |
243 } | |
244 input.accept = uploadcare.imagesOnly ? 'image/*' : ''; | |
245 input.onchange = async function() { | |
246 function onError(status,text) { | |
247 hideImg(); | |
248 if( text ) | |
249 alert(text); | |
250 if( uploadcare.onError ) | |
251 uploadcare.onError(status,text); | |
252 } | |
253 function callback2(uuid,filename) { | |
254 hideImg(); | |
255 callback(uuid,filename) | |
256 } | |
257 let info = { file: input.files[0] }; | |
258 input.value = null; | |
259 await infoCompress(info); | |
260 if( !info.canceled ) { | |
261 showImg(); | |
262 call(info.compressed,info.compressedName,callback2,onError); | |
263 } | |
264 }; | |
265 input.click(); | |
266 }; | |
267 } |