preloader.js 3.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134
  1. const Preloader = /** @constructor */ function () { // eslint-disable-line no-unused-vars
  2. function getTrackedResponse(response, load_status) {
  3. function onloadprogress(reader, controller) {
  4. return reader.read().then(function (result) {
  5. if (load_status.done) {
  6. return Promise.resolve();
  7. }
  8. if (result.value) {
  9. controller.enqueue(result.value);
  10. load_status.loaded += result.value.length;
  11. }
  12. if (!result.done) {
  13. return onloadprogress(reader, controller);
  14. }
  15. load_status.done = true;
  16. return Promise.resolve();
  17. });
  18. }
  19. const reader = response.body.getReader();
  20. return new Response(new ReadableStream({
  21. start: function (controller) {
  22. onloadprogress(reader, controller).then(function () {
  23. controller.close();
  24. });
  25. },
  26. }), { headers: response.headers });
  27. }
  28. function loadFetch(file, tracker, fileSize, raw) {
  29. tracker[file] = {
  30. total: fileSize || 0,
  31. loaded: 0,
  32. done: false,
  33. };
  34. return fetch(file).then(function (response) {
  35. if (!response.ok) {
  36. return Promise.reject(new Error(`Failed loading file '${file}'`));
  37. }
  38. const tr = getTrackedResponse(response, tracker[file]);
  39. if (raw) {
  40. return Promise.resolve(tr);
  41. }
  42. return tr.arrayBuffer();
  43. });
  44. }
  45. function retry(func, attempts = 1) {
  46. function onerror(err) {
  47. if (attempts <= 1) {
  48. return Promise.reject(err);
  49. }
  50. return new Promise(function (resolve, reject) {
  51. setTimeout(function () {
  52. retry(func, attempts - 1).then(resolve).catch(reject);
  53. }, 1000);
  54. });
  55. }
  56. return func().catch(onerror);
  57. }
  58. const DOWNLOAD_ATTEMPTS_MAX = 4;
  59. const loadingFiles = {};
  60. const lastProgress = { loaded: 0, total: 0 };
  61. let progressFunc = null;
  62. const animateProgress = function () {
  63. let loaded = 0;
  64. let total = 0;
  65. let totalIsValid = true;
  66. let progressIsFinal = true;
  67. Object.keys(loadingFiles).forEach(function (file) {
  68. const stat = loadingFiles[file];
  69. if (!stat.done) {
  70. progressIsFinal = false;
  71. }
  72. if (!totalIsValid || stat.total === 0) {
  73. totalIsValid = false;
  74. total = 0;
  75. } else {
  76. total += stat.total;
  77. }
  78. loaded += stat.loaded;
  79. });
  80. if (loaded !== lastProgress.loaded || total !== lastProgress.total) {
  81. lastProgress.loaded = loaded;
  82. lastProgress.total = total;
  83. if (typeof progressFunc === 'function') {
  84. progressFunc(loaded, total);
  85. }
  86. }
  87. if (!progressIsFinal) {
  88. requestAnimationFrame(animateProgress);
  89. }
  90. };
  91. this.animateProgress = animateProgress;
  92. this.setProgressFunc = function (callback) {
  93. progressFunc = callback;
  94. };
  95. this.loadPromise = function (file, fileSize, raw = false) {
  96. return retry(loadFetch.bind(null, file, loadingFiles, fileSize, raw), DOWNLOAD_ATTEMPTS_MAX);
  97. };
  98. this.preloadedFiles = [];
  99. this.preload = function (pathOrBuffer, destPath, fileSize) {
  100. let buffer = null;
  101. if (typeof pathOrBuffer === 'string') {
  102. const me = this;
  103. return this.loadPromise(pathOrBuffer, fileSize).then(function (buf) {
  104. me.preloadedFiles.push({
  105. path: destPath || pathOrBuffer,
  106. buffer: buf,
  107. });
  108. return Promise.resolve();
  109. });
  110. } else if (pathOrBuffer instanceof ArrayBuffer) {
  111. buffer = new Uint8Array(pathOrBuffer);
  112. } else if (ArrayBuffer.isView(pathOrBuffer)) {
  113. buffer = new Uint8Array(pathOrBuffer.buffer);
  114. }
  115. if (buffer) {
  116. this.preloadedFiles.push({
  117. path: destPath,
  118. buffer: pathOrBuffer,
  119. });
  120. return Promise.resolve();
  121. }
  122. return Promise.reject(new Error('Invalid object for preloading'));
  123. };
  124. };