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 }