file-manager.js 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113
  1. /* global window, XMLHttpRequest */
  2. import AbstractFileManager from '../less/environment/abstract-file-manager.js';
  3. let options;
  4. let logger;
  5. let fileCache = {};
  6. // TODOS - move log somewhere. pathDiff and doing something similar in node. use pathDiff in the other browser file for the initial load
  7. class FileManager extends AbstractFileManager {
  8. alwaysMakePathsAbsolute() {
  9. return true;
  10. }
  11. join(basePath, laterPath) {
  12. if (!basePath) {
  13. return laterPath;
  14. }
  15. return this.extractUrlParts(laterPath, basePath).path;
  16. }
  17. doXHR(url, type, callback, errback) {
  18. const xhr = new XMLHttpRequest();
  19. const async = options.isFileProtocol ? options.fileAsync : true;
  20. if (typeof xhr.overrideMimeType === 'function') {
  21. xhr.overrideMimeType('text/css');
  22. }
  23. logger.debug(`XHR: Getting '${url}'`);
  24. xhr.open('GET', url, async);
  25. xhr.setRequestHeader('Accept', type || 'text/x-less, text/css; q=0.9, */*; q=0.5');
  26. xhr.send(null);
  27. function handleResponse(xhr, callback, errback) {
  28. if (xhr.status >= 200 && xhr.status < 300) {
  29. callback(xhr.responseText,
  30. xhr.getResponseHeader('Last-Modified'));
  31. } else if (typeof errback === 'function') {
  32. errback(xhr.status, url);
  33. }
  34. }
  35. if (options.isFileProtocol && !options.fileAsync) {
  36. if (xhr.status === 0 || (xhr.status >= 200 && xhr.status < 300)) {
  37. callback(xhr.responseText);
  38. } else {
  39. errback(xhr.status, url);
  40. }
  41. } else if (async) {
  42. xhr.onreadystatechange = () => {
  43. if (xhr.readyState == 4) {
  44. handleResponse(xhr, callback, errback);
  45. }
  46. };
  47. } else {
  48. handleResponse(xhr, callback, errback);
  49. }
  50. }
  51. supports() {
  52. return true;
  53. }
  54. clearFileCache() {
  55. fileCache = {};
  56. }
  57. loadFile(filename, currentDirectory, options, environment) {
  58. // TODO: Add prefix support like less-node?
  59. // What about multiple paths?
  60. if (currentDirectory && !this.isPathAbsolute(filename)) {
  61. filename = currentDirectory + filename;
  62. }
  63. filename = options.ext ? this.tryAppendExtension(filename, options.ext) : filename;
  64. options = options || {};
  65. // sheet may be set to the stylesheet for the initial load or a collection of properties including
  66. // some context variables for imports
  67. const hrefParts = this.extractUrlParts(filename, window.location.href);
  68. const href = hrefParts.url;
  69. const self = this;
  70. return new Promise((resolve, reject) => {
  71. if (options.useFileCache && fileCache[href]) {
  72. try {
  73. const lessText = fileCache[href];
  74. return resolve({ contents: lessText, filename: href, webInfo: { lastModified: new Date() }});
  75. } catch (e) {
  76. return reject({ filename: href, message: `Error loading file ${href} error was ${e.message}` });
  77. }
  78. }
  79. self.doXHR(href, options.mime, function doXHRCallback(data, lastModified) {
  80. // per file cache
  81. fileCache[href] = data;
  82. // Use remote copy (re-parse)
  83. resolve({ contents: data, filename: href, webInfo: { lastModified }});
  84. }, function doXHRError(status, url) {
  85. reject({ type: 'File', message: `'${url}' wasn't found (${status})`, href });
  86. });
  87. });
  88. }
  89. }
  90. export default (opts, log) => {
  91. options = opts;
  92. logger = log;
  93. return FileManager;
  94. }