editor-settings.js 5.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. (function() { // eslint-disable-line strict
  2. 'use strict'; // eslint-disable-line strict
  3. function dirname(path) {
  4. const ndx = path.lastIndexOf('/');
  5. return path.substring(0, ndx + 1);
  6. }
  7. function getPrefix(url) {
  8. const u = new URL(url, window.location.href);
  9. const prefix = u.origin + dirname(u.pathname);
  10. return prefix;
  11. }
  12. function getRootPrefix(url) {
  13. const u = new URL(url, window.location.href);
  14. return u.origin;
  15. }
  16. function removeDotDotSlash(url) {
  17. // assumes a well formed URL. In other words: 'https://..//foo.html" is a bad URL and this code would fail.
  18. const parts = url.split('/');
  19. for (;;) {
  20. const dotDotNdx = parts.indexOf('..');
  21. if (dotDotNdx < 0) {
  22. break;
  23. }
  24. parts.splice(dotDotNdx - 1, 2);
  25. }
  26. const newUrl = parts.join('/');
  27. return newUrl;
  28. }
  29. /**
  30. * Fix any local URLs into fully qualified urls.
  31. *
  32. * Examples:
  33. * resources/image.jpg -> https://domain.org/webgl/resouces/image.jpg
  34. * /3rdparty/lib.js -> https://domain.org/3rdparty/lib.js
  35. *
  36. * The reason is (a) we're running the code as via blobUrl and nothing is relative to a blob.
  37. * (b) we can upload to jsfiddle/codepen and so need to link back to the files.
  38. *
  39. * This is all kind of hacky in that it's just a bunch of regular expressions looking
  40. * for matches.
  41. *
  42. * @param {string} url The URL of the file source.
  43. * @param {string} source An HTML file or JavaScript file
  44. * @returns {string} the source after having urls fixed.
  45. */
  46. function fixSourceLinks(url, source) {
  47. const srcRE = /(src=)(")(.*?)(")()/g;
  48. const linkRE = /(href=)(")(.*?)(")()/g;
  49. const imageSrcRE = /((?:image|img)\.src = )(")(.*?)(")()/g;
  50. const loaderLoadRE = /(loader\.load[a-z]*\s*\(\s*)('|")(.*?)('|")/ig;
  51. const loaderArrayLoadRE = /(loader\.load[a-z]*\(\[)([\s\S]*?)(\])/ig;
  52. const loadFileRE = /(loadFile\s*\(\s*)('|")(.*?)('|")/ig;
  53. const threejsUrlRE = /(.*?)('|")([^"']*?)('|")([^'"]*?)(\/\*\s+threejs.org:\s+url\s+\*\/)/ig;
  54. const arrayLineRE = /^(\s*["|'])([\s\S]*?)(["|']*$)/;
  55. const urlPropRE = /(url:\s*)('|")(.*?)('|")/g;
  56. const workerRE = /(new\s+Worker\s*\(\s*)('|")(.*?)('|")/g;
  57. const importScriptsRE = /(importScripts\s*\(\s*)('|")(.*?)('|")/g;
  58. const moduleRE = /(import.*?)('|")(.*?)('|")/g;
  59. const prefix = getPrefix(url);
  60. const rootPrefix = getRootPrefix(url);
  61. function addCorrectPrefix(url) {
  62. return (url.startsWith('/'))
  63. ? `${rootPrefix}${url}`
  64. : removeDotDotSlash((prefix + url).replace(/\/.\//g, '/'));
  65. }
  66. function addPrefix(url) {
  67. return url.indexOf('://') < 0 && !url.startsWith('data:') && url[0] !== '?'
  68. ? removeDotDotSlash(addCorrectPrefix(url))
  69. : url;
  70. }
  71. function makeLinkFDedQuotes(match, fn, q1, url, q2) {
  72. return fn + q1 + addPrefix(url) + q2;
  73. }
  74. function makeTaggedFDedQuotes(match, start, q1, url, q2, suffix) {
  75. return start + q1 + addPrefix(url) + q2 + suffix;
  76. }
  77. function makeFDedQuotesModule(match, start, q1, url, q2) {
  78. // modules require relative paths or fully qualified, otherwise they are module names
  79. return `${start}${q1}${url.startsWith('.') ? addPrefix(url) : url}${q2}`;
  80. }
  81. function makeArrayLinksFDed(match, prefix, arrayStr, suffix) {
  82. const lines = arrayStr.split(',').map((line) => {
  83. const m = arrayLineRE.exec(line);
  84. return m
  85. ? `${m[1]}${addPrefix(m[2])}${m[3]}`
  86. : line;
  87. });
  88. return `${prefix}${lines.join(',')}${suffix}`;
  89. }
  90. source = source.replace(srcRE, makeTaggedFDedQuotes);
  91. source = source.replace(linkRE, makeTaggedFDedQuotes);
  92. source = source.replace(imageSrcRE, makeTaggedFDedQuotes);
  93. source = source.replace(urlPropRE, makeLinkFDedQuotes);
  94. source = source.replace(loadFileRE, makeLinkFDedQuotes);
  95. source = source.replace(loaderLoadRE, makeLinkFDedQuotes);
  96. source = source.replace(workerRE, makeLinkFDedQuotes);
  97. source = source.replace(importScriptsRE, makeLinkFDedQuotes);
  98. source = source.replace(loaderArrayLoadRE, makeArrayLinksFDed);
  99. source = source.replace(threejsUrlRE, makeTaggedFDedQuotes);
  100. source = source.replace(moduleRE, makeFDedQuotesModule);
  101. return source;
  102. }
  103. /**
  104. * Called after parsing to give a change to update htmlParts
  105. * @param {string} html The main page html turned into a template with the <style>, <script> and <body> parts extracted
  106. * @param {Object<string, HTMLPart>} htmlParts All the extracted parts
  107. * @return {string} The modified html template
  108. */
  109. function extraHTMLParsing(html /* , htmlParts */) {
  110. return html;
  111. }
  112. /**
  113. * Change JavaScript before uploading code to JSFiddle/Codepen
  114. *
  115. * @param {string} js JavaScript source
  116. * @returns {string} The JavaScript source with any fixes applied.
  117. */
  118. let version;
  119. async function fixJSForCodeSite(js) {
  120. const moduleRE = /(import.*?)('|")(.*?)('|")/g;
  121. // convert https://threejs.org/build/three.module.js -> https://unpkg.com/three@<version>
  122. // convert https://threejs.org/examples/jsm/.?? -> https://unpkg.com/three@<version>/examples/jsm/.??
  123. if (!version) {
  124. try {
  125. const res = await fetch('https://raw.githubusercontent.com/mrdoob/three.js/master/package.json');
  126. const json = await res.json();
  127. version = json.version;
  128. } catch (e) {
  129. console.error(e);
  130. }
  131. }
  132. function addVersion(href) {
  133. if (href.startsWith(window.location.origin)) {
  134. if (href.includes('/build/three.module.js')) {
  135. return `https://unpkg.com/three@${version}`;
  136. } else if (href.includes('/examples/jsm/')) {
  137. const url = new URL(href);
  138. return `https://unpkg.com/three@${version}${url.pathname}${url.search}${url.hash}`;
  139. }
  140. }
  141. return href;
  142. }
  143. function addVersionToURL(match, start, q1, url, q2) {
  144. return start + q1 + addVersion(url) + q2;
  145. }
  146. if (version !== undefined) {
  147. js = js.replace(moduleRE, addVersionToURL);
  148. }
  149. return js;
  150. }
  151. window.lessonEditorSettings = {
  152. extraHTMLParsing,
  153. fixSourceLinks,
  154. fixJSForCodeSite,
  155. runOnResize: false,
  156. lessonSettings: {
  157. glDebug: false,
  158. },
  159. tags: ['three.js'],
  160. name: 'three.js',
  161. icon: '/files/icon.svg',
  162. };
  163. }());