preloader.js 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  1. var Preloader = /** @constructor */ function() { // eslint-disable-line no-unused-vars
  2. const loadXHR = function(resolve, reject, file, tracker, attempts) {
  3. const xhr = new XMLHttpRequest();
  4. tracker[file] = {
  5. total: 0,
  6. loaded: 0,
  7. final: false,
  8. };
  9. xhr.onerror = function() {
  10. if (attempts <= 1) {
  11. reject(new Error("Failed loading file '" + file + "'"));
  12. } else {
  13. setTimeout(function () {
  14. loadXHR(resolve, reject, file, tracker, attempts - 1);
  15. }, 1000);
  16. }
  17. };
  18. xhr.onabort = function() {
  19. tracker[file].final = true;
  20. reject(new Error("Loading file '" + file + "' was aborted."));
  21. };
  22. xhr.onloadstart = function(ev) {
  23. tracker[file].total = ev.total;
  24. tracker[file].loaded = ev.loaded;
  25. };
  26. xhr.onprogress = function(ev) {
  27. tracker[file].loaded = ev.loaded;
  28. tracker[file].total = ev.total;
  29. };
  30. xhr.onload = function() {
  31. if (xhr.status >= 400) {
  32. if (xhr.status < 500 || attempts <= 1) {
  33. reject(new Error("Failed loading file '" + file + "': " + xhr.statusText));
  34. xhr.abort();
  35. } else {
  36. setTimeout(function () {
  37. loadXHR(resolve, reject, file, tracker, attempts - 1);
  38. }, 1000);
  39. }
  40. } else {
  41. tracker[file].final = true;
  42. resolve(xhr);
  43. }
  44. };
  45. // Make request.
  46. xhr.open('GET', file);
  47. if (!file.endsWith('.js')) {
  48. xhr.responseType = 'arraybuffer';
  49. }
  50. xhr.send();
  51. };
  52. const DOWNLOAD_ATTEMPTS_MAX = 4;
  53. const loadingFiles = {};
  54. const lastProgress = { loaded: 0, total: 0 };
  55. let progressFunc = null;
  56. const animateProgress = function() {
  57. var loaded = 0;
  58. var total = 0;
  59. var totalIsValid = true;
  60. var progressIsFinal = true;
  61. Object.keys(loadingFiles).forEach(function(file) {
  62. const stat = loadingFiles[file];
  63. if (!stat.final) {
  64. progressIsFinal = false;
  65. }
  66. if (!totalIsValid || stat.total === 0) {
  67. totalIsValid = false;
  68. total = 0;
  69. } else {
  70. total += stat.total;
  71. }
  72. loaded += stat.loaded;
  73. });
  74. if (loaded !== lastProgress.loaded || total !== lastProgress.total) {
  75. lastProgress.loaded = loaded;
  76. lastProgress.total = total;
  77. if (typeof progressFunc === 'function')
  78. progressFunc(loaded, total);
  79. }
  80. if (!progressIsFinal)
  81. requestAnimationFrame(animateProgress);
  82. }
  83. this.animateProgress = animateProgress;
  84. this.setProgressFunc = function(callback) {
  85. progressFunc = callback;
  86. }
  87. this.loadPromise = function(file) {
  88. return new Promise(function(resolve, reject) {
  89. loadXHR(resolve, reject, file, loadingFiles, DOWNLOAD_ATTEMPTS_MAX);
  90. });
  91. }
  92. this.preloadedFiles = [];
  93. this.preload = function(pathOrBuffer, destPath) {
  94. let buffer = null;
  95. if (typeof pathOrBuffer === 'string') {
  96. var me = this;
  97. return this.loadPromise(pathOrBuffer).then(function(xhr) {
  98. me.preloadedFiles.push({
  99. path: destPath || pathOrBuffer,
  100. buffer: xhr.response
  101. });
  102. return Promise.resolve();
  103. });
  104. } else if (pathOrBuffer instanceof ArrayBuffer) {
  105. buffer = new Uint8Array(pathOrBuffer);
  106. } else if (ArrayBuffer.isView(pathOrBuffer)) {
  107. buffer = new Uint8Array(pathOrBuffer.buffer);
  108. }
  109. if (buffer) {
  110. this.preloadedFiles.push({
  111. path: destPath,
  112. buffer: pathOrBuffer
  113. });
  114. return Promise.resolve();
  115. } else {
  116. return Promise.reject(new Error("Invalid object for preloading"));
  117. }
  118. };
  119. };