compressorjs-next 2.0.2 → 2.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Compressor.js Next v2.0.2
2
+ * Compressor.js Next v2.0.3
3
3
  * https://github.com/j9t/compressorjs-next
4
4
  *
5
5
  * Copyright 2018–2024 Chen Fengyuan
@@ -549,10 +549,7 @@ class Compressor {
549
549
  type: mimeType,
550
550
  lastModified: date.getTime()
551
551
  });
552
- this.result = result;
553
- if (options.success) {
554
- options.success.call(this, result);
555
- }
552
+ this.succeed(result);
556
553
  };
557
554
  reader.onabort = () => {
558
555
  this.fail(new Error('Aborted to read the image with FileReader.'));
@@ -569,10 +566,7 @@ class Compressor {
569
566
  // Defer callback to match the normal async flow
570
567
  Promise.resolve().then(() => {
571
568
  if (this.aborted) return;
572
- this.result = file;
573
- if (options.success) {
574
- options.success.call(this, file);
575
- }
569
+ this.succeed(file);
576
570
  });
577
571
  }
578
572
  return;
@@ -761,67 +755,19 @@ class Compressor {
761
755
  result
762
756
  });
763
757
  if (blob && isJPEGImage && options.retainExif && this.exif && this.exif.length > 0) {
764
- const next = arrayBuffer => {
758
+ this.readBlobAsArrayBuffer(blob, arrayBuffer => {
765
759
  if (this.aborted) return;
766
760
  const withExif = insertExif(arrayBuffer, this.exif);
767
761
  done(uint8ArrayToBlob(withExif, options.mimeType));
768
- };
769
- if (blob.arrayBuffer) {
770
- blob.arrayBuffer().then(next).catch(() => {
771
- if (this.aborted) return;
772
- this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
773
- });
774
- } else {
775
- const reader = new FileReader();
776
- this.reader = reader;
777
- reader.onload = ({
778
- target
779
- }) => {
780
- next(target.result);
781
- };
782
- reader.onabort = () => {
783
- this.fail(new Error('Aborted to read the compressed image with FileReader.'));
784
- };
785
- reader.onerror = () => {
786
- this.fail(new Error('Failed to read the compressed image with FileReader.'));
787
- };
788
- reader.onloadend = () => {
789
- this.reader = null;
790
- };
791
- reader.readAsArrayBuffer(blob);
792
- }
762
+ });
793
763
  } else if (blob && isJPEGImage && !options.retainExif) {
794
764
  // Strip any EXIF that may be present in the canvas output
795
765
  // (most browsers strip it automatically, but WebKit preserves the
796
766
  // source EXIF—this ensures consistent, privacy-safe output)
797
- const next = arrayBuffer => {
767
+ this.readBlobAsArrayBuffer(blob, arrayBuffer => {
798
768
  if (this.aborted) return;
799
769
  done(uint8ArrayToBlob(stripExif(arrayBuffer), options.mimeType));
800
- };
801
- if (blob.arrayBuffer) {
802
- blob.arrayBuffer().then(next).catch(() => {
803
- if (this.aborted) return;
804
- this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
805
- });
806
- } else {
807
- const reader = new FileReader();
808
- this.reader = reader;
809
- reader.onload = ({
810
- target
811
- }) => {
812
- next(target.result);
813
- };
814
- reader.onabort = () => {
815
- this.fail(new Error('Aborted to read the compressed image with FileReader.'));
816
- };
817
- reader.onerror = () => {
818
- this.fail(new Error('Failed to read the compressed image with FileReader.'));
819
- };
820
- reader.onloadend = () => {
821
- this.reader = null;
822
- };
823
- reader.readAsArrayBuffer(blob);
824
- }
770
+ });
825
771
  } else {
826
772
  done(blob);
827
773
  }
@@ -875,17 +821,11 @@ class Compressor {
875
821
  type: file.type,
876
822
  lastModified: file.lastModified || Date.now()
877
823
  });
878
- this.result = stripped;
879
- if (options.success) {
880
- options.success.call(this, stripped);
881
- }
824
+ this.succeed(stripped);
882
825
  }).catch(err => {
883
826
  if (this.aborted) return;
884
827
  console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}${err?.message ? `: ${err.message}` : ''}`);
885
- this.result = file;
886
- if (options.success) {
887
- options.success.call(this, file);
888
- }
828
+ this.succeed(file);
889
829
  });
890
830
  } else {
891
831
  const reader = new FileReader();
@@ -899,10 +839,7 @@ class Compressor {
899
839
  type: file.type,
900
840
  lastModified: file.lastModified || Date.now()
901
841
  });
902
- this.result = stripped;
903
- if (options.success) {
904
- options.success.call(this, stripped);
905
- }
842
+ this.succeed(stripped);
906
843
  };
907
844
  reader.onabort = () => {
908
845
  this.fail(new Error('Aborted to read the original file with FileReader.'));
@@ -910,10 +847,7 @@ class Compressor {
910
847
  reader.onerror = () => {
911
848
  if (this.aborted) return;
912
849
  console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}`);
913
- this.result = file;
914
- if (options.success) {
915
- options.success.call(this, file);
916
- }
850
+ this.succeed(file);
917
851
  };
918
852
  reader.onloadend = () => {
919
853
  this.reader = null;
@@ -922,9 +856,46 @@ class Compressor {
922
856
  }
923
857
  return;
924
858
  }
859
+ this.succeed(result);
860
+ }
861
+ readBlobAsArrayBuffer(blob, next) {
862
+ if (blob.arrayBuffer) {
863
+ blob.arrayBuffer().then(next).catch(() => {
864
+ // Defer to a macrotask so any `abort()` scheduled via `setTimeout()`
865
+ // in a `drew()` hook has a chance to set `this.aborted` first
866
+ setTimeout(() => {
867
+ if (this.aborted) return;
868
+ this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
869
+ }, 0);
870
+ });
871
+ } else {
872
+ const reader = new FileReader();
873
+ this.reader = reader;
874
+ reader.onload = ({
875
+ target
876
+ }) => {
877
+ try {
878
+ next(target.result);
879
+ } catch (err) {
880
+ if (!this.aborted) this.fail(err);
881
+ }
882
+ };
883
+ reader.onabort = () => {
884
+ this.fail(new Error('Aborted to read the compressed image with FileReader.'));
885
+ };
886
+ reader.onerror = () => {
887
+ this.fail(new Error('Failed to read the compressed image with FileReader.'));
888
+ };
889
+ reader.onloadend = () => {
890
+ this.reader = null;
891
+ };
892
+ reader.readAsArrayBuffer(blob);
893
+ }
894
+ }
895
+ succeed(result) {
925
896
  this.result = result;
926
- if (options.success) {
927
- options.success.call(this, result);
897
+ if (this.options.success) {
898
+ this.options.success.call(this, result);
928
899
  }
929
900
  }
930
901
  fail(err) {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Compressor.js Next v2.0.2
2
+ * Compressor.js Next v2.0.3
3
3
  * https://github.com/j9t/compressorjs-next
4
4
  *
5
5
  * Copyright 2018–2024 Chen Fengyuan
@@ -547,10 +547,7 @@ class Compressor {
547
547
  type: mimeType,
548
548
  lastModified: date.getTime()
549
549
  });
550
- this.result = result;
551
- if (options.success) {
552
- options.success.call(this, result);
553
- }
550
+ this.succeed(result);
554
551
  };
555
552
  reader.onabort = () => {
556
553
  this.fail(new Error('Aborted to read the image with FileReader.'));
@@ -567,10 +564,7 @@ class Compressor {
567
564
  // Defer callback to match the normal async flow
568
565
  Promise.resolve().then(() => {
569
566
  if (this.aborted) return;
570
- this.result = file;
571
- if (options.success) {
572
- options.success.call(this, file);
573
- }
567
+ this.succeed(file);
574
568
  });
575
569
  }
576
570
  return;
@@ -759,67 +753,19 @@ class Compressor {
759
753
  result
760
754
  });
761
755
  if (blob && isJPEGImage && options.retainExif && this.exif && this.exif.length > 0) {
762
- const next = arrayBuffer => {
756
+ this.readBlobAsArrayBuffer(blob, arrayBuffer => {
763
757
  if (this.aborted) return;
764
758
  const withExif = insertExif(arrayBuffer, this.exif);
765
759
  done(uint8ArrayToBlob(withExif, options.mimeType));
766
- };
767
- if (blob.arrayBuffer) {
768
- blob.arrayBuffer().then(next).catch(() => {
769
- if (this.aborted) return;
770
- this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
771
- });
772
- } else {
773
- const reader = new FileReader();
774
- this.reader = reader;
775
- reader.onload = ({
776
- target
777
- }) => {
778
- next(target.result);
779
- };
780
- reader.onabort = () => {
781
- this.fail(new Error('Aborted to read the compressed image with FileReader.'));
782
- };
783
- reader.onerror = () => {
784
- this.fail(new Error('Failed to read the compressed image with FileReader.'));
785
- };
786
- reader.onloadend = () => {
787
- this.reader = null;
788
- };
789
- reader.readAsArrayBuffer(blob);
790
- }
760
+ });
791
761
  } else if (blob && isJPEGImage && !options.retainExif) {
792
762
  // Strip any EXIF that may be present in the canvas output
793
763
  // (most browsers strip it automatically, but WebKit preserves the
794
764
  // source EXIF—this ensures consistent, privacy-safe output)
795
- const next = arrayBuffer => {
765
+ this.readBlobAsArrayBuffer(blob, arrayBuffer => {
796
766
  if (this.aborted) return;
797
767
  done(uint8ArrayToBlob(stripExif(arrayBuffer), options.mimeType));
798
- };
799
- if (blob.arrayBuffer) {
800
- blob.arrayBuffer().then(next).catch(() => {
801
- if (this.aborted) return;
802
- this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
803
- });
804
- } else {
805
- const reader = new FileReader();
806
- this.reader = reader;
807
- reader.onload = ({
808
- target
809
- }) => {
810
- next(target.result);
811
- };
812
- reader.onabort = () => {
813
- this.fail(new Error('Aborted to read the compressed image with FileReader.'));
814
- };
815
- reader.onerror = () => {
816
- this.fail(new Error('Failed to read the compressed image with FileReader.'));
817
- };
818
- reader.onloadend = () => {
819
- this.reader = null;
820
- };
821
- reader.readAsArrayBuffer(blob);
822
- }
768
+ });
823
769
  } else {
824
770
  done(blob);
825
771
  }
@@ -873,17 +819,11 @@ class Compressor {
873
819
  type: file.type,
874
820
  lastModified: file.lastModified || Date.now()
875
821
  });
876
- this.result = stripped;
877
- if (options.success) {
878
- options.success.call(this, stripped);
879
- }
822
+ this.succeed(stripped);
880
823
  }).catch(err => {
881
824
  if (this.aborted) return;
882
825
  console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}${err?.message ? `: ${err.message}` : ''}`);
883
- this.result = file;
884
- if (options.success) {
885
- options.success.call(this, file);
886
- }
826
+ this.succeed(file);
887
827
  });
888
828
  } else {
889
829
  const reader = new FileReader();
@@ -897,10 +837,7 @@ class Compressor {
897
837
  type: file.type,
898
838
  lastModified: file.lastModified || Date.now()
899
839
  });
900
- this.result = stripped;
901
- if (options.success) {
902
- options.success.call(this, stripped);
903
- }
840
+ this.succeed(stripped);
904
841
  };
905
842
  reader.onabort = () => {
906
843
  this.fail(new Error('Aborted to read the original file with FileReader.'));
@@ -908,10 +845,7 @@ class Compressor {
908
845
  reader.onerror = () => {
909
846
  if (this.aborted) return;
910
847
  console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}`);
911
- this.result = file;
912
- if (options.success) {
913
- options.success.call(this, file);
914
- }
848
+ this.succeed(file);
915
849
  };
916
850
  reader.onloadend = () => {
917
851
  this.reader = null;
@@ -920,9 +854,46 @@ class Compressor {
920
854
  }
921
855
  return;
922
856
  }
857
+ this.succeed(result);
858
+ }
859
+ readBlobAsArrayBuffer(blob, next) {
860
+ if (blob.arrayBuffer) {
861
+ blob.arrayBuffer().then(next).catch(() => {
862
+ // Defer to a macrotask so any `abort()` scheduled via `setTimeout()`
863
+ // in a `drew()` hook has a chance to set `this.aborted` first
864
+ setTimeout(() => {
865
+ if (this.aborted) return;
866
+ this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
867
+ }, 0);
868
+ });
869
+ } else {
870
+ const reader = new FileReader();
871
+ this.reader = reader;
872
+ reader.onload = ({
873
+ target
874
+ }) => {
875
+ try {
876
+ next(target.result);
877
+ } catch (err) {
878
+ if (!this.aborted) this.fail(err);
879
+ }
880
+ };
881
+ reader.onabort = () => {
882
+ this.fail(new Error('Aborted to read the compressed image with FileReader.'));
883
+ };
884
+ reader.onerror = () => {
885
+ this.fail(new Error('Failed to read the compressed image with FileReader.'));
886
+ };
887
+ reader.onloadend = () => {
888
+ this.reader = null;
889
+ };
890
+ reader.readAsArrayBuffer(blob);
891
+ }
892
+ }
893
+ succeed(result) {
923
894
  this.result = result;
924
- if (options.success) {
925
- options.success.call(this, result);
895
+ if (this.options.success) {
896
+ this.options.success.call(this, result);
926
897
  }
927
898
  }
928
899
  fail(err) {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Compressor.js Next v2.0.2
2
+ * Compressor.js Next v2.0.3
3
3
  * https://github.com/j9t/compressorjs-next
4
4
  *
5
5
  * Copyright 2018–2024 Chen Fengyuan
@@ -553,10 +553,7 @@
553
553
  type: mimeType,
554
554
  lastModified: date.getTime()
555
555
  });
556
- this.result = result;
557
- if (options.success) {
558
- options.success.call(this, result);
559
- }
556
+ this.succeed(result);
560
557
  };
561
558
  reader.onabort = () => {
562
559
  this.fail(new Error('Aborted to read the image with FileReader.'));
@@ -573,10 +570,7 @@
573
570
  // Defer callback to match the normal async flow
574
571
  Promise.resolve().then(() => {
575
572
  if (this.aborted) return;
576
- this.result = file;
577
- if (options.success) {
578
- options.success.call(this, file);
579
- }
573
+ this.succeed(file);
580
574
  });
581
575
  }
582
576
  return;
@@ -765,67 +759,19 @@
765
759
  result
766
760
  });
767
761
  if (blob && isJPEGImage && options.retainExif && this.exif && this.exif.length > 0) {
768
- const next = arrayBuffer => {
762
+ this.readBlobAsArrayBuffer(blob, arrayBuffer => {
769
763
  if (this.aborted) return;
770
764
  const withExif = insertExif(arrayBuffer, this.exif);
771
765
  done(uint8ArrayToBlob(withExif, options.mimeType));
772
- };
773
- if (blob.arrayBuffer) {
774
- blob.arrayBuffer().then(next).catch(() => {
775
- if (this.aborted) return;
776
- this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
777
- });
778
- } else {
779
- const reader = new FileReader();
780
- this.reader = reader;
781
- reader.onload = ({
782
- target
783
- }) => {
784
- next(target.result);
785
- };
786
- reader.onabort = () => {
787
- this.fail(new Error('Aborted to read the compressed image with FileReader.'));
788
- };
789
- reader.onerror = () => {
790
- this.fail(new Error('Failed to read the compressed image with FileReader.'));
791
- };
792
- reader.onloadend = () => {
793
- this.reader = null;
794
- };
795
- reader.readAsArrayBuffer(blob);
796
- }
766
+ });
797
767
  } else if (blob && isJPEGImage && !options.retainExif) {
798
768
  // Strip any EXIF that may be present in the canvas output
799
769
  // (most browsers strip it automatically, but WebKit preserves the
800
770
  // source EXIF—this ensures consistent, privacy-safe output)
801
- const next = arrayBuffer => {
771
+ this.readBlobAsArrayBuffer(blob, arrayBuffer => {
802
772
  if (this.aborted) return;
803
773
  done(uint8ArrayToBlob(stripExif(arrayBuffer), options.mimeType));
804
- };
805
- if (blob.arrayBuffer) {
806
- blob.arrayBuffer().then(next).catch(() => {
807
- if (this.aborted) return;
808
- this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
809
- });
810
- } else {
811
- const reader = new FileReader();
812
- this.reader = reader;
813
- reader.onload = ({
814
- target
815
- }) => {
816
- next(target.result);
817
- };
818
- reader.onabort = () => {
819
- this.fail(new Error('Aborted to read the compressed image with FileReader.'));
820
- };
821
- reader.onerror = () => {
822
- this.fail(new Error('Failed to read the compressed image with FileReader.'));
823
- };
824
- reader.onloadend = () => {
825
- this.reader = null;
826
- };
827
- reader.readAsArrayBuffer(blob);
828
- }
774
+ });
829
775
  } else {
830
776
  done(blob);
831
777
  }
@@ -879,17 +825,11 @@
879
825
  type: file.type,
880
826
  lastModified: file.lastModified || Date.now()
881
827
  });
882
- this.result = stripped;
883
- if (options.success) {
884
- options.success.call(this, stripped);
885
- }
828
+ this.succeed(stripped);
886
829
  }).catch(err => {
887
830
  if (this.aborted) return;
888
831
  console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}${err?.message ? `: ${err.message}` : ''}`);
889
- this.result = file;
890
- if (options.success) {
891
- options.success.call(this, file);
892
- }
832
+ this.succeed(file);
893
833
  });
894
834
  } else {
895
835
  const reader = new FileReader();
@@ -903,10 +843,7 @@
903
843
  type: file.type,
904
844
  lastModified: file.lastModified || Date.now()
905
845
  });
906
- this.result = stripped;
907
- if (options.success) {
908
- options.success.call(this, stripped);
909
- }
846
+ this.succeed(stripped);
910
847
  };
911
848
  reader.onabort = () => {
912
849
  this.fail(new Error('Aborted to read the original file with FileReader.'));
@@ -914,10 +851,7 @@
914
851
  reader.onerror = () => {
915
852
  if (this.aborted) return;
916
853
  console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}`);
917
- this.result = file;
918
- if (options.success) {
919
- options.success.call(this, file);
920
- }
854
+ this.succeed(file);
921
855
  };
922
856
  reader.onloadend = () => {
923
857
  this.reader = null;
@@ -926,9 +860,46 @@
926
860
  }
927
861
  return;
928
862
  }
863
+ this.succeed(result);
864
+ }
865
+ readBlobAsArrayBuffer(blob, next) {
866
+ if (blob.arrayBuffer) {
867
+ blob.arrayBuffer().then(next).catch(() => {
868
+ // Defer to a macrotask so any `abort()` scheduled via `setTimeout()`
869
+ // in a `drew()` hook has a chance to set `this.aborted` first
870
+ setTimeout(() => {
871
+ if (this.aborted) return;
872
+ this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
873
+ }, 0);
874
+ });
875
+ } else {
876
+ const reader = new FileReader();
877
+ this.reader = reader;
878
+ reader.onload = ({
879
+ target
880
+ }) => {
881
+ try {
882
+ next(target.result);
883
+ } catch (err) {
884
+ if (!this.aborted) this.fail(err);
885
+ }
886
+ };
887
+ reader.onabort = () => {
888
+ this.fail(new Error('Aborted to read the compressed image with FileReader.'));
889
+ };
890
+ reader.onerror = () => {
891
+ this.fail(new Error('Failed to read the compressed image with FileReader.'));
892
+ };
893
+ reader.onloadend = () => {
894
+ this.reader = null;
895
+ };
896
+ reader.readAsArrayBuffer(blob);
897
+ }
898
+ }
899
+ succeed(result) {
929
900
  this.result = result;
930
- if (options.success) {
931
- options.success.call(this, result);
901
+ if (this.options.success) {
902
+ this.options.success.call(this, result);
932
903
  }
933
904
  }
934
905
  fail(err) {
@@ -1,5 +1,5 @@
1
1
  /*!
2
- * Compressor.js Next v2.0.2
2
+ * Compressor.js Next v2.0.3
3
3
  * https://github.com/j9t/compressorjs-next
4
4
  *
5
5
  * Copyright 2018–2024 Chen Fengyuan
@@ -7,4 +7,4 @@
7
7
  *
8
8
  * Released under the MIT license.
9
9
  */
10
- !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Compressor=t()}(this,function(){"use strict";var e={strict:!0,retainExif:!1,maxWidth:1/0,maxHeight:1/0,minWidth:0,minHeight:0,width:void 0,height:void 0,resize:"none",quality:.8,mimeType:"auto",convertTypes:[],convertSize:5e6,beforeDraw:null,drew:null,success:null,error:null};const t="undefined"!=typeof window&&void 0!==window.document?window:{},i=e=>e>0&&e<1/0,r=/^image\/.+$/;function a(e){return r.test(e)}const{fromCharCode:n}=String;function s(e){const t=new DataView(e);let i;try{let e,r,a;if(255===t.getUint8(0)&&216===t.getUint8(1)){const e=t.byteLength;let i=2;for(;i+1<e;){if(255===t.getUint8(i)&&225===t.getUint8(i+1)){r=i;break}i+=1}}if(r){const i=r+10;if("Exif"===function(e,t,i){let r,a="";for(i+=t,r=t;r<i;r+=1)a+=n(e.getUint8(r));return a}(t,r+4,4)){const r=t.getUint16(i);if(e=18761===r,(e||19789===r)&&42===t.getUint16(i+2,e)){const r=t.getUint32(i+4,e);r>=8&&(a=i+r)}}}if(a){const r=t.getUint16(a,e);let n,s;for(s=0;s<r;s+=1)if(n=a+12*s+2,274===t.getUint16(n,e)){n+=8,i=t.getUint16(n,e),t.setUint16(n,1,e);break}}}catch{i=1}return i}let o;function h(e){const t=new DataView(e),{byteLength:i}=t,r=[];if(i<4||255!==t.getUint8(0)||216!==t.getUint8(1))return new Uint8Array(e);r.push(new Uint8Array(e,0,2));let a=2;for(;a+3<i;){const n=t.getUint8(a),s=t.getUint8(a+1);if(255!==n)break;if(218===s){r.push(new Uint8Array(e,a));break}if(a+3>=i)break;const o=t.getUint16(a+2);if(o<2)break;const h=a+2+o;if(h>i)break;225!==s&&r.push(new Uint8Array(e,a,h-a)),a=h}const n=r.reduce((e,t)=>e+t.length,0),s=new Uint8Array(n);let o=0;for(const e of r)s.set(e,o),o+=e.length;return s}const l=/\.\d*(?:0|9){12}\d*$/;function d(e,t=1e11){return l.test(e)?Math.round(e*t)/t:e}function c({aspectRatio:e,height:t,width:r},a="none"){const n=i(r),s=i(t);if(n&&s){const i=t*e;("contain"===a||"none"===a)&&i>r||"cover"===a&&i<r?t=r/e:r=t*e}else n?t=r/e:s&&(r=t*e);return{width:r,height:t}}function f(e,t){return new Blob([e],{type:t})}const{ArrayBuffer:u,FileReader:g}=t,m=t.URL||t.webkitURL,w=/\.\w+$/;return class{constructor(t,i){this.file=t,this.exif=[],this.image=new Image,this.options={...e,...i},this.mimeTypeSet=i&&i.mimeType&&a(i.mimeType),this.aborted=!1,this.canvasFallback=!1,this.result=null,this.url=null,this.init()}init(){const{file:e,options:t}=this;if(!(e instanceof Blob))return void this.fail(new Error("The first argument must be a File or Blob object."));const i=e.type;if(!a(i))return void this.fail(new Error("The first argument must be an image File or Blob object."));if(!m||!g)return void this.fail(new Error("The current browser does not support image compression."));if(u||(t.retainExif=!1),!function(){if(void 0!==o)return o;try{const e=document.createElement("canvas");e.width=4,e.height=4;const t=e.getContext("2d"),i=t.createImageData(e.width,e.height);for(let e=0;e<i.data.length;e+=4)i.data[e]=e,i.data[e+1]=1,i.data[e+2]=2,i.data[e+3]=255;t.putImageData(i,0,0);const r=t.getImageData(0,0,e.width,e.height);o=r.data.every((e,t)=>{const i=t%4;return 0===i?e===(255&t):1===i?1===e:2===i?2===e:255===e})}catch{o=!1}return o}()){if(console.warn("Compressor.js Next: Canvas data is unreliable (e.g., due to browser fingerprinting resistance)—compression, resizing, and format conversion are unavailable"),this.canvasFallback=!0,"image/jpeg"!==i||t.retainExif)Promise.resolve().then(()=>{this.aborted||(this.result=e,t.success&&t.success.call(this,e))});else{const r=new g;this.reader=r,r.onload=({target:r})=>{if(this.aborted)return;let a;try{a=f(h(r.result),i)}catch{return void this.fail(new Error("Failed to process the image data."))}const n=new Date,s=new File([a],e.name||"",{type:i,lastModified:n.getTime()});this.result=s,t.success&&t.success.call(this,s)},r.onabort=()=>{this.fail(new Error("Aborted to read the image with FileReader."))},r.onerror=()=>{this.fail(new Error("Failed to read the image with FileReader."))},r.onloadend=()=>{this.reader=null},r.readAsArrayBuffer(e)}return}if("image/jpeg"===i&&t.retainExif){const t=new g;this.reader=t,t.onload=({target:t})=>{this.aborted||(s(t.result),this.exif=function(e){const t=new DataView(e),{byteLength:i}=t,r=[];let a=0;for(;a+3<i;){const e=t.getUint8(a),n=t.getUint8(a+1);if(255===e&&218===n)break;if(255===e&&216===n)a+=2;else{const s=a+t.getUint16(a+2)+2;if(255===e&&225===n)for(let e=a;e<s&&e<i;e+=1)r.push(t.getUint8(e));a=s}}return r}(t.result),this.url=m.createObjectURL(e),this.load({url:this.url}))},t.onabort=()=>{this.fail(new Error("Aborted to read the image with FileReader."))},t.onerror=()=>{this.fail(new Error("Failed to read the image with FileReader."))},t.onloadend=()=>{this.reader=null},t.readAsArrayBuffer(e)}else this.url=m.createObjectURL(e),this.load({url:this.url})}load(e){const{file:i,image:r}=this;r.onload=()=>{this.draw({...e,naturalWidth:r.naturalWidth,naturalHeight:r.naturalHeight})},r.onabort=()=>{this.fail(new Error("Aborted to load the image."))},r.onerror=()=>{this.fail(new Error("Failed to load the image."))},t.navigator&&/(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(t.navigator.userAgent)&&(r.crossOrigin="anonymous"),r.alt=i.name,r.src=e.url}draw({naturalWidth:e,naturalHeight:t}){const{file:r,image:n,options:s}=this,o=document.createElement("canvas"),l=o.getContext("2d"),u=("contain"===s.resize||"cover"===s.resize)&&i(s.width)&&i(s.height);let m=Math.max(s.maxWidth,0)||1/0,w=Math.max(s.maxHeight,0)||1/0,p=Math.max(s.minWidth,0)||0,b=Math.max(s.minHeight,0)||0,y=e/t,{width:U,height:v}=s;u&&(y=U/v),({width:m,height:w}=c({aspectRatio:y,width:m,height:w},"contain")),({width:p,height:b}=c({aspectRatio:y,width:p,height:b},"cover")),u?({width:U,height:v}=c({aspectRatio:y,width:U,height:v},s.resize)):({width:U=e,height:v=t}=c({aspectRatio:y,width:U,height:v})),U=Math.floor(d(Math.min(Math.max(U,p),m))),v=Math.floor(d(Math.min(Math.max(v,b),w)));const x=[];if(u){const{width:i,height:r}=c({aspectRatio:y,width:e,height:t},{contain:"cover",cover:"contain"}[s.resize]),a=(e-i)/2,n=(t-r)/2;x.push(a,n,i,r)}x.push(0,0,U,v),o.width=U,o.height=v,a(s.mimeType)||(s.mimeType=r.type);let E="transparent";!this.mimeTypeSet&&r.size>s.convertSize&&s.convertTypes.indexOf(s.mimeType)>=0&&(s.mimeType="image/jpeg");const F="image/jpeg"===s.mimeType;if(F&&(E="#fff"),l.fillStyle=E,l.fillRect(0,0,U,v),s.beforeDraw&&s.beforeDraw.call(this,l,o),this.aborted)return;if(l.drawImage(n,...x),s.drew&&s.drew.call(this,l,o),this.aborted)return;o.toBlob(i=>{if(!this.aborted){const r=i=>this.done({naturalWidth:e,naturalHeight:t,result:i});if(i&&F&&s.retainExif&&this.exif&&this.exif.length>0){const e=e=>{if(this.aborted)return;const t=function(e,t){const i=new DataView(e),r=new Uint8Array(e);if(255!==i.getUint8(2)||224!==i.getUint8(3))return r;const a=4+i.getUint16(4),n=r.byteLength-a,s=new Uint8Array(2+t.length+n);s[0]=255,s[1]=216;for(let e=0;e<t.length;e+=1)s[2+e]=t[e];return s.set(r.subarray(a),2+t.length),s}(e,this.exif);r(f(t,s.mimeType))};if(i.arrayBuffer)i.arrayBuffer().then(e).catch(()=>{this.aborted||this.fail(new Error("Failed to read the compressed image with `Blob.arrayBuffer()`."))});else{const t=new g;this.reader=t,t.onload=({target:t})=>{e(t.result)},t.onabort=()=>{this.fail(new Error("Aborted to read the compressed image with FileReader."))},t.onerror=()=>{this.fail(new Error("Failed to read the compressed image with FileReader."))},t.onloadend=()=>{this.reader=null},t.readAsArrayBuffer(i)}}else if(i&&F&&!s.retainExif){const e=e=>{this.aborted||r(f(h(e),s.mimeType))};if(i.arrayBuffer)i.arrayBuffer().then(e).catch(()=>{this.aborted||this.fail(new Error("Failed to read the compressed image with `Blob.arrayBuffer()`."))});else{const t=new g;this.reader=t,t.onload=({target:t})=>{e(t.result)},t.onabort=()=>{this.fail(new Error("Aborted to read the compressed image with FileReader."))},t.onerror=()=>{this.fail(new Error("Failed to read the compressed image with FileReader."))},t.onloadend=()=>{this.reader=null},t.readAsArrayBuffer(i)}}else r(i)}},s.mimeType,s.quality)}done({naturalWidth:e,naturalHeight:t,result:i}){const{file:r,options:n}=this;this.revokeUrl();let s=!1;if(i)if(n.strict&&!n.retainExif&&i.size>r.size&&n.mimeType===r.type&&!(n.width>e||n.height>t||n.minWidth>e||n.minHeight>t||n.maxWidth<e||n.maxHeight<t))i=r,s=!0;else{const e=new Date;let t=r.name||"";t&&i.type!==r.type&&(t=t.replace(w,function(e){let t=a(e)?e.slice(6):"";return"jpeg"===t&&(t="jpg"),`.${t}`}(i.type))),i=new File([i],t,{type:i.type,lastModified:e.getTime()})}else console.warn("Compressor.js Next: Canvas produced no output—returning the original image"),i=r;if(s&&"image/jpeg"===r.type)if(r.arrayBuffer)r.arrayBuffer().then(e=>{if(this.aborted)return;const t=f(h(e),r.type),i=new File([t],r.name||"",{type:r.type,lastModified:r.lastModified||Date.now()});this.result=i,n.success&&n.success.call(this,i)}).catch(e=>{this.aborted||(console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${r.name?` [${r.name}]`:""}${e?.message?`: ${e.message}`:""}`),this.result=r,n.success&&n.success.call(this,r))});else{const e=new g;this.reader=e,e.onload=({target:e})=>{if(this.aborted)return;const t=f(h(e.result),r.type),i=new File([t],r.name||"",{type:r.type,lastModified:r.lastModified||Date.now()});this.result=i,n.success&&n.success.call(this,i)},e.onabort=()=>{this.fail(new Error("Aborted to read the original file with FileReader."))},e.onerror=()=>{this.aborted||(console.warn("Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact"+(r.name?` [${r.name}]`:"")),this.result=r,n.success&&n.success.call(this,r))},e.onloadend=()=>{this.reader=null},e.readAsArrayBuffer(r)}else this.result=i,n.success&&n.success.call(this,i)}fail(e){const{options:t}=this;if(this.revokeUrl(),!t.error)throw e;t.error.call(this,e)}revokeUrl(){m&&this.url&&(m.revokeObjectURL(this.url),this.url=null)}abort(){this.aborted||(this.aborted=!0,this.reader?this.reader.abort():this.image.complete?this.fail(new Error("The compression process has been aborted.")):(this.image.onload=null,this.image.onerror=null,this.image.onabort=null,this.fail(new Error("Aborted to load the image."))))}static setDefaults(t){Object.assign(e,t)}}});
10
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?module.exports=t():"function"==typeof define&&define.amd?define(t):(e="undefined"!=typeof globalThis?globalThis:e||self).Compressor=t()}(this,function(){"use strict";var e={strict:!0,retainExif:!1,maxWidth:1/0,maxHeight:1/0,minWidth:0,minHeight:0,width:void 0,height:void 0,resize:"none",quality:.8,mimeType:"auto",convertTypes:[],convertSize:5e6,beforeDraw:null,drew:null,success:null,error:null};const t="undefined"!=typeof window&&void 0!==window.document?window:{},i=e=>e>0&&e<1/0,r=/^image\/.+$/;function a(e){return r.test(e)}const{fromCharCode:n}=String;function o(e){const t=new DataView(e);let i;try{let e,r,a;if(255===t.getUint8(0)&&216===t.getUint8(1)){const e=t.byteLength;let i=2;for(;i+1<e;){if(255===t.getUint8(i)&&225===t.getUint8(i+1)){r=i;break}i+=1}}if(r){const i=r+10;if("Exif"===function(e,t,i){let r,a="";for(i+=t,r=t;r<i;r+=1)a+=n(e.getUint8(r));return a}(t,r+4,4)){const r=t.getUint16(i);if(e=18761===r,(e||19789===r)&&42===t.getUint16(i+2,e)){const r=t.getUint32(i+4,e);r>=8&&(a=i+r)}}}if(a){const r=t.getUint16(a,e);let n,o;for(o=0;o<r;o+=1)if(n=a+12*o+2,274===t.getUint16(n,e)){n+=8,i=t.getUint16(n,e),t.setUint16(n,1,e);break}}}catch{i=1}return i}let s;function h(e){const t=new DataView(e),{byteLength:i}=t,r=[];if(i<4||255!==t.getUint8(0)||216!==t.getUint8(1))return new Uint8Array(e);r.push(new Uint8Array(e,0,2));let a=2;for(;a+3<i;){const n=t.getUint8(a),o=t.getUint8(a+1);if(255!==n)break;if(218===o){r.push(new Uint8Array(e,a));break}if(a+3>=i)break;const s=t.getUint16(a+2);if(s<2)break;const h=a+2+s;if(h>i)break;225!==o&&r.push(new Uint8Array(e,a,h-a)),a=h}const n=r.reduce((e,t)=>e+t.length,0),o=new Uint8Array(n);let s=0;for(const e of r)o.set(e,s),s+=e.length;return o}const l=/\.\d*(?:0|9){12}\d*$/;function d(e,t=1e11){return l.test(e)?Math.round(e*t)/t:e}function c({aspectRatio:e,height:t,width:r},a="none"){const n=i(r),o=i(t);if(n&&o){const i=t*e;("contain"===a||"none"===a)&&i>r||"cover"===a&&i<r?t=r/e:r=t*e}else n?t=r/e:o&&(r=t*e);return{width:r,height:t}}function f(e,t){return new Blob([e],{type:t})}const{ArrayBuffer:u,FileReader:g}=t,m=t.URL||t.webkitURL,w=/\.\w+$/;return class{constructor(t,i){this.file=t,this.exif=[],this.image=new Image,this.options={...e,...i},this.mimeTypeSet=i&&i.mimeType&&a(i.mimeType),this.aborted=!1,this.canvasFallback=!1,this.result=null,this.url=null,this.init()}init(){const{file:e,options:t}=this;if(!(e instanceof Blob))return void this.fail(new Error("The first argument must be a File or Blob object."));const i=e.type;if(!a(i))return void this.fail(new Error("The first argument must be an image File or Blob object."));if(!m||!g)return void this.fail(new Error("The current browser does not support image compression."));if(u||(t.retainExif=!1),!function(){if(void 0!==s)return s;try{const e=document.createElement("canvas");e.width=4,e.height=4;const t=e.getContext("2d"),i=t.createImageData(e.width,e.height);for(let e=0;e<i.data.length;e+=4)i.data[e]=e,i.data[e+1]=1,i.data[e+2]=2,i.data[e+3]=255;t.putImageData(i,0,0);const r=t.getImageData(0,0,e.width,e.height);s=r.data.every((e,t)=>{const i=t%4;return 0===i?e===(255&t):1===i?1===e:2===i?2===e:255===e})}catch{s=!1}return s}()){if(console.warn("Compressor.js Next: Canvas data is unreliable (e.g., due to browser fingerprinting resistance)—compression, resizing, and format conversion are unavailable"),this.canvasFallback=!0,"image/jpeg"!==i||t.retainExif)Promise.resolve().then(()=>{this.aborted||this.succeed(e)});else{const t=new g;this.reader=t,t.onload=({target:t})=>{if(this.aborted)return;let r;try{r=f(h(t.result),i)}catch{return void this.fail(new Error("Failed to process the image data."))}const a=new Date,n=new File([r],e.name||"",{type:i,lastModified:a.getTime()});this.succeed(n)},t.onabort=()=>{this.fail(new Error("Aborted to read the image with FileReader."))},t.onerror=()=>{this.fail(new Error("Failed to read the image with FileReader."))},t.onloadend=()=>{this.reader=null},t.readAsArrayBuffer(e)}return}if("image/jpeg"===i&&t.retainExif){const t=new g;this.reader=t,t.onload=({target:t})=>{this.aborted||(o(t.result),this.exif=function(e){const t=new DataView(e),{byteLength:i}=t,r=[];let a=0;for(;a+3<i;){const e=t.getUint8(a),n=t.getUint8(a+1);if(255===e&&218===n)break;if(255===e&&216===n)a+=2;else{const o=a+t.getUint16(a+2)+2;if(255===e&&225===n)for(let e=a;e<o&&e<i;e+=1)r.push(t.getUint8(e));a=o}}return r}(t.result),this.url=m.createObjectURL(e),this.load({url:this.url}))},t.onabort=()=>{this.fail(new Error("Aborted to read the image with FileReader."))},t.onerror=()=>{this.fail(new Error("Failed to read the image with FileReader."))},t.onloadend=()=>{this.reader=null},t.readAsArrayBuffer(e)}else this.url=m.createObjectURL(e),this.load({url:this.url})}load(e){const{file:i,image:r}=this;r.onload=()=>{this.draw({...e,naturalWidth:r.naturalWidth,naturalHeight:r.naturalHeight})},r.onabort=()=>{this.fail(new Error("Aborted to load the image."))},r.onerror=()=>{this.fail(new Error("Failed to load the image."))},t.navigator&&/(?:iPad|iPhone|iPod).*?AppleWebKit/i.test(t.navigator.userAgent)&&(r.crossOrigin="anonymous"),r.alt=i.name,r.src=e.url}draw({naturalWidth:e,naturalHeight:t}){const{file:r,image:n,options:o}=this,s=document.createElement("canvas"),l=s.getContext("2d"),u=("contain"===o.resize||"cover"===o.resize)&&i(o.width)&&i(o.height);let g=Math.max(o.maxWidth,0)||1/0,m=Math.max(o.maxHeight,0)||1/0,w=Math.max(o.minWidth,0)||0,p=Math.max(o.minHeight,0)||0,b=e/t,{width:y,height:U}=o;u&&(b=y/U),({width:g,height:m}=c({aspectRatio:b,width:g,height:m},"contain")),({width:w,height:p}=c({aspectRatio:b,width:w,height:p},"cover")),u?({width:y,height:U}=c({aspectRatio:b,width:y,height:U},o.resize)):({width:y=e,height:U=t}=c({aspectRatio:b,width:y,height:U})),y=Math.floor(d(Math.min(Math.max(y,w),g))),U=Math.floor(d(Math.min(Math.max(U,p),m)));const v=[];if(u){const{width:i,height:r}=c({aspectRatio:b,width:e,height:t},{contain:"cover",cover:"contain"}[o.resize]),a=(e-i)/2,n=(t-r)/2;v.push(a,n,i,r)}v.push(0,0,y,U),s.width=y,s.height=U,a(o.mimeType)||(o.mimeType=r.type);let x="transparent";!this.mimeTypeSet&&r.size>o.convertSize&&o.convertTypes.indexOf(o.mimeType)>=0&&(o.mimeType="image/jpeg");const A="image/jpeg"===o.mimeType;if(A&&(x="#fff"),l.fillStyle=x,l.fillRect(0,0,y,U),o.beforeDraw&&o.beforeDraw.call(this,l,s),this.aborted)return;if(l.drawImage(n,...v),o.drew&&o.drew.call(this,l,s),this.aborted)return;s.toBlob(i=>{if(!this.aborted){const r=i=>this.done({naturalWidth:e,naturalHeight:t,result:i});i&&A&&o.retainExif&&this.exif&&this.exif.length>0?this.readBlobAsArrayBuffer(i,e=>{if(this.aborted)return;const t=function(e,t){const i=new DataView(e),r=new Uint8Array(e);if(255!==i.getUint8(2)||224!==i.getUint8(3))return r;const a=4+i.getUint16(4),n=r.byteLength-a,o=new Uint8Array(2+t.length+n);o[0]=255,o[1]=216;for(let e=0;e<t.length;e+=1)o[2+e]=t[e];return o.set(r.subarray(a),2+t.length),o}(e,this.exif);r(f(t,o.mimeType))}):i&&A&&!o.retainExif?this.readBlobAsArrayBuffer(i,e=>{this.aborted||r(f(h(e),o.mimeType))}):r(i)}},o.mimeType,o.quality)}done({naturalWidth:e,naturalHeight:t,result:i}){const{file:r,options:n}=this;this.revokeUrl();let o=!1;if(i)if(n.strict&&!n.retainExif&&i.size>r.size&&n.mimeType===r.type&&!(n.width>e||n.height>t||n.minWidth>e||n.minHeight>t||n.maxWidth<e||n.maxHeight<t))i=r,o=!0;else{const e=new Date;let t=r.name||"";t&&i.type!==r.type&&(t=t.replace(w,function(e){let t=a(e)?e.slice(6):"";return"jpeg"===t&&(t="jpg"),`.${t}`}(i.type))),i=new File([i],t,{type:i.type,lastModified:e.getTime()})}else console.warn("Compressor.js Next: Canvas produced no output—returning the original image"),i=r;if(o&&"image/jpeg"===r.type)if(r.arrayBuffer)r.arrayBuffer().then(e=>{if(this.aborted)return;const t=f(h(e),r.type),i=new File([t],r.name||"",{type:r.type,lastModified:r.lastModified||Date.now()});this.succeed(i)}).catch(e=>{this.aborted||(console.warn(`Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${r.name?` [${r.name}]`:""}${e?.message?`: ${e.message}`:""}`),this.succeed(r))});else{const e=new g;this.reader=e,e.onload=({target:e})=>{if(this.aborted)return;const t=f(h(e.result),r.type),i=new File([t],r.name||"",{type:r.type,lastModified:r.lastModified||Date.now()});this.succeed(i)},e.onabort=()=>{this.fail(new Error("Aborted to read the original file with FileReader."))},e.onerror=()=>{this.aborted||(console.warn("Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact"+(r.name?` [${r.name}]`:"")),this.succeed(r))},e.onloadend=()=>{this.reader=null},e.readAsArrayBuffer(r)}else this.succeed(i)}readBlobAsArrayBuffer(e,t){if(e.arrayBuffer)e.arrayBuffer().then(t).catch(()=>{setTimeout(()=>{this.aborted||this.fail(new Error("Failed to read the compressed image with `Blob.arrayBuffer()`."))},0)});else{const i=new g;this.reader=i,i.onload=({target:e})=>{try{t(e.result)}catch(e){this.aborted||this.fail(e)}},i.onabort=()=>{this.fail(new Error("Aborted to read the compressed image with FileReader."))},i.onerror=()=>{this.fail(new Error("Failed to read the compressed image with FileReader."))},i.onloadend=()=>{this.reader=null},i.readAsArrayBuffer(e)}}succeed(e){this.result=e,this.options.success&&this.options.success.call(this,e)}fail(e){const{options:t}=this;if(this.revokeUrl(),!t.error)throw e;t.error.call(this,e)}revokeUrl(){m&&this.url&&(m.revokeObjectURL(this.url),this.url=null)}abort(){this.aborted||(this.aborted=!0,this.reader?this.reader.abort():this.image.complete?this.fail(new Error("The compression process has been aborted.")):(this.image.onload=null,this.image.onerror=null,this.image.onabort=null,this.fail(new Error("Aborted to load the image."))))}static setDefaults(t){Object.assign(e,t)}}});
package/package.json CHANGED
@@ -18,19 +18,19 @@
18
18
  "@rollup/plugin-babel": "^7.0.0",
19
19
  "@rollup/plugin-commonjs": "^29.0.2",
20
20
  "@rollup/plugin-node-resolve": "^16.0.3",
21
- "@vitest/browser-playwright": "^4.0.18",
22
- "@vitest/coverage-istanbul": "^4.0.18",
21
+ "@vitest/browser-playwright": "^4.1.0",
22
+ "@vitest/coverage-istanbul": "^4.1.0",
23
23
  "del-cli": "^7.0.0",
24
- "eslint": "^10.0.0",
24
+ "eslint": "^10.1.0",
25
25
  "globals": "^17.0.0",
26
26
  "husky": "^9.1.7",
27
- "playwright": "^1.58.2",
28
- "rollup": "^4.59.0",
29
- "stylelint": "^17.1.1",
27
+ "playwright": "^1.59.1",
28
+ "rollup": "^4.60.0",
29
+ "stylelint": "^17.6.0",
30
30
  "stylelint-config-standard": "^40.0.0",
31
- "stylelint-order": "^7.0.1",
31
+ "stylelint-order": "^8.1.1",
32
32
  "terser": "^5.46.0",
33
- "vitest": "^4.0.18"
33
+ "vitest": "^4.1.0"
34
34
  },
35
35
  "exports": {
36
36
  ".": {
@@ -93,5 +93,5 @@
93
93
  "sideEffects": false,
94
94
  "type": "module",
95
95
  "types": "types/index.d.ts",
96
- "version": "2.0.2"
96
+ "version": "2.0.3"
97
97
  }
package/src/index.js CHANGED
@@ -98,11 +98,7 @@ export default class Compressor {
98
98
  lastModified: date.getTime(),
99
99
  });
100
100
 
101
- this.result = result;
102
-
103
- if (options.success) {
104
- options.success.call(this, result);
105
- }
101
+ this.succeed(result);
106
102
  };
107
103
  reader.onabort = () => {
108
104
  this.fail(new Error('Aborted to read the image with FileReader.'));
@@ -119,12 +115,7 @@ export default class Compressor {
119
115
  // Defer callback to match the normal async flow
120
116
  Promise.resolve().then(() => {
121
117
  if (this.aborted) return;
122
-
123
- this.result = file;
124
-
125
- if (options.success) {
126
- options.success.call(this, file);
127
- }
118
+ this.succeed(file);
128
119
  });
129
120
  }
130
121
 
@@ -312,67 +303,19 @@ export default class Compressor {
312
303
  });
313
304
 
314
305
  if (blob && isJPEGImage && options.retainExif && this.exif && this.exif.length > 0) {
315
- const next = (arrayBuffer) => {
306
+ this.readBlobAsArrayBuffer(blob, (arrayBuffer) => {
316
307
  if (this.aborted) return;
317
308
  const withExif = insertExif(arrayBuffer, this.exif);
318
309
  done(uint8ArrayToBlob(withExif, options.mimeType));
319
- };
320
-
321
- if (blob.arrayBuffer) {
322
- blob.arrayBuffer().then(next).catch(() => {
323
- if (this.aborted) return;
324
- this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
325
- });
326
- } else {
327
- const reader = new FileReader();
328
-
329
- this.reader = reader;
330
- reader.onload = ({ target }) => {
331
- next(target.result);
332
- };
333
- reader.onabort = () => {
334
- this.fail(new Error('Aborted to read the compressed image with FileReader.'));
335
- };
336
- reader.onerror = () => {
337
- this.fail(new Error('Failed to read the compressed image with FileReader.'));
338
- };
339
- reader.onloadend = () => {
340
- this.reader = null;
341
- };
342
- reader.readAsArrayBuffer(blob);
343
- }
310
+ });
344
311
  } else if (blob && isJPEGImage && !options.retainExif) {
345
312
  // Strip any EXIF that may be present in the canvas output
346
313
  // (most browsers strip it automatically, but WebKit preserves the
347
314
  // source EXIF—this ensures consistent, privacy-safe output)
348
- const next = (arrayBuffer) => {
315
+ this.readBlobAsArrayBuffer(blob, (arrayBuffer) => {
349
316
  if (this.aborted) return;
350
317
  done(uint8ArrayToBlob(stripExif(arrayBuffer), options.mimeType));
351
- };
352
-
353
- if (blob.arrayBuffer) {
354
- blob.arrayBuffer().then(next).catch(() => {
355
- if (this.aborted) return;
356
- this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
357
- });
358
- } else {
359
- const reader = new FileReader();
360
-
361
- this.reader = reader;
362
- reader.onload = ({ target }) => {
363
- next(target.result);
364
- };
365
- reader.onabort = () => {
366
- this.fail(new Error('Aborted to read the compressed image with FileReader.'));
367
- };
368
- reader.onerror = () => {
369
- this.fail(new Error('Failed to read the compressed image with FileReader.'));
370
- };
371
- reader.onloadend = () => {
372
- this.reader = null;
373
- };
374
- reader.readAsArrayBuffer(blob);
375
- }
318
+ });
376
319
  } else {
377
320
  done(blob);
378
321
  }
@@ -447,11 +390,7 @@ export default class Compressor {
447
390
  lastModified: file.lastModified || Date.now(),
448
391
  });
449
392
 
450
- this.result = stripped;
451
-
452
- if (options.success) {
453
- options.success.call(this, stripped);
454
- }
393
+ this.succeed(stripped);
455
394
  }).catch((err) => {
456
395
  if (this.aborted) return;
457
396
 
@@ -459,11 +398,7 @@ export default class Compressor {
459
398
  `Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}${err?.message ? `: ${err.message}` : ''}`,
460
399
  );
461
400
 
462
- this.result = file;
463
-
464
- if (options.success) {
465
- options.success.call(this, file);
466
- }
401
+ this.succeed(file);
467
402
  });
468
403
  } else {
469
404
  const reader = new FileReader();
@@ -478,11 +413,7 @@ export default class Compressor {
478
413
  lastModified: file.lastModified || Date.now(),
479
414
  });
480
415
 
481
- this.result = stripped;
482
-
483
- if (options.success) {
484
- options.success.call(this, stripped);
485
- }
416
+ this.succeed(stripped);
486
417
  };
487
418
  reader.onabort = () => {
488
419
  this.fail(new Error('Aborted to read the original file with FileReader.'));
@@ -494,11 +425,7 @@ export default class Compressor {
494
425
  `Compressor.js Next: Failed to strip EXIF from original file—returning original with EXIF intact${file.name ? ` [${file.name}]` : ''}`,
495
426
  );
496
427
 
497
- this.result = file;
498
-
499
- if (options.success) {
500
- options.success.call(this, file);
501
- }
428
+ this.succeed(file);
502
429
  };
503
430
  reader.onloadend = () => {
504
431
  this.reader = null;
@@ -509,10 +436,48 @@ export default class Compressor {
509
436
  return;
510
437
  }
511
438
 
439
+ this.succeed(result);
440
+ }
441
+
442
+ readBlobAsArrayBuffer(blob, next) {
443
+ if (blob.arrayBuffer) {
444
+ blob.arrayBuffer().then(next).catch(() => {
445
+ // Defer to a macrotask so any `abort()` scheduled via `setTimeout()`
446
+ // in a `drew()` hook has a chance to set `this.aborted` first
447
+ setTimeout(() => {
448
+ if (this.aborted) return;
449
+ this.fail(new Error('Failed to read the compressed image with `Blob.arrayBuffer()`.'));
450
+ }, 0);
451
+ });
452
+ } else {
453
+ const reader = new FileReader();
454
+
455
+ this.reader = reader;
456
+ reader.onload = ({ target }) => {
457
+ try {
458
+ next(target.result);
459
+ } catch (err) {
460
+ if (!this.aborted) this.fail(err);
461
+ }
462
+ };
463
+ reader.onabort = () => {
464
+ this.fail(new Error('Aborted to read the compressed image with FileReader.'));
465
+ };
466
+ reader.onerror = () => {
467
+ this.fail(new Error('Failed to read the compressed image with FileReader.'));
468
+ };
469
+ reader.onloadend = () => {
470
+ this.reader = null;
471
+ };
472
+ reader.readAsArrayBuffer(blob);
473
+ }
474
+ }
475
+
476
+ succeed(result) {
512
477
  this.result = result;
513
478
 
514
- if (options.success) {
515
- options.success.call(this, result);
479
+ if (this.options.success) {
480
+ this.options.success.call(this, result);
516
481
  }
517
482
  }
518
483