You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
242 lines
6.1 KiB
JavaScript
242 lines
6.1 KiB
JavaScript
2 months ago
|
|
||
|
( function () { // eslint-disable-line strict
|
||
|
|
||
|
'use strict'; // eslint-disable-line strict
|
||
|
|
||
|
function dirname( path ) {
|
||
|
|
||
|
const ndx = path.lastIndexOf( '/' );
|
||
|
return path.substring( 0, ndx + 1 );
|
||
|
|
||
|
}
|
||
|
|
||
|
function getPrefix( url ) {
|
||
|
|
||
|
const u = new URL( url, window.location.href );
|
||
|
const prefix = u.origin + dirname( u.pathname );
|
||
|
return prefix;
|
||
|
|
||
|
}
|
||
|
|
||
|
function getRootPrefix( url ) {
|
||
|
|
||
|
const u = new URL( url, window.location.href );
|
||
|
return u.origin;
|
||
|
|
||
|
}
|
||
|
|
||
|
function removeDotDotSlash( url ) {
|
||
|
|
||
|
// assumes a well formed URL. In other words: 'https://..//foo.html" is a bad URL and this code would fail.
|
||
|
const parts = url.split( '/' );
|
||
|
for ( ;; ) {
|
||
|
|
||
|
const dotDotNdx = parts.indexOf( '..' );
|
||
|
if ( dotDotNdx < 0 ) {
|
||
|
|
||
|
break;
|
||
|
|
||
|
}
|
||
|
|
||
|
parts.splice( dotDotNdx - 1, 2 );
|
||
|
|
||
|
}
|
||
|
|
||
|
const newUrl = parts.join( '/' );
|
||
|
return newUrl;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Fix any local URLs into fully qualified urls.
|
||
|
*
|
||
|
* Examples:
|
||
|
* resources/image.jpg -> https://domain.org/webgl/resouces/image.jpg
|
||
|
* /3rdparty/lib.js -> https://domain.org/3rdparty/lib.js
|
||
|
*
|
||
|
* The reason is (a) we're running the code as via blobUrl and nothing is relative to a blob.
|
||
|
* (b) we can upload to jsfiddle/codepen and so need to link back to the files.
|
||
|
*
|
||
|
* This is all kind of hacky in that it's just a bunch of regular expressions looking
|
||
|
* for matches.
|
||
|
*
|
||
|
* @param {string} url The URL of the file source.
|
||
|
* @param {string} source An HTML file or JavaScript file
|
||
|
* @returns {string} the source after having urls fixed.
|
||
|
*/
|
||
|
function fixSourceLinks( url, source ) {
|
||
|
|
||
|
const srcRE = /(src=)(")(.*?)(")()/g;
|
||
|
const linkRE = /(href=)(")(.*?)(")()/g;
|
||
|
const imageSrcRE = /((?:image|img)\.src = )(")(.*?)(")()/g;
|
||
|
const loaderLoadRE = /(loader\.load[a-z]*\s*\(\s*)('|")(.*?)('|")/ig;
|
||
|
const loaderArrayLoadRE = /(loader\.load[a-z]*\(\s*\[)([\s\S]*?)(\])/ig;
|
||
|
const loadFileRE = /(loadFile\s*\(\s*)('|")(.*?)('|")/ig;
|
||
|
const threejsUrlRE = /(.*?)('|")([^"']*?)('|")([^'"]*?)(\/\*\s+threejs.org:\s+url\s+\*\/)/ig;
|
||
|
const arrayLineRE = /^(\s*["|'])([\s\S]*?)(["|']*$)/;
|
||
|
const urlPropRE = /(url:\s*)('|")(.*?)('|")/g;
|
||
|
const workerRE = /(new\s+Worker\s*\(\s*)('|")(.*?)('|")/g;
|
||
|
const importScriptsRE = /(importScripts\s*\(\s*)('|")(.*?)('|")/g;
|
||
|
const moduleRE = /(import.*?)('|")(.*?)('|")/g;
|
||
|
const prefix = getPrefix( url );
|
||
|
const rootPrefix = getRootPrefix( url );
|
||
|
|
||
|
function addCorrectPrefix( url ) {
|
||
|
|
||
|
return ( url.startsWith( '/' ) )
|
||
|
? `${rootPrefix}${url}`
|
||
|
: removeDotDotSlash( ( prefix + url ).replace( /\/.\//g, '/' ) );
|
||
|
|
||
|
}
|
||
|
|
||
|
function addPrefix( url ) {
|
||
|
|
||
|
return url.indexOf( '://' ) < 0 && ! url.startsWith( 'data:' ) && url[ 0 ] !== '?'
|
||
|
? removeDotDotSlash( addCorrectPrefix( url ) )
|
||
|
: url;
|
||
|
|
||
|
}
|
||
|
|
||
|
function makeLinkFDedQuotes( match, fn, q1, url, q2 ) {
|
||
|
|
||
|
return fn + q1 + addPrefix( url ) + q2;
|
||
|
|
||
|
}
|
||
|
|
||
|
function makeTaggedFDedQuotes( match, start, q1, url, q2, suffix ) {
|
||
|
|
||
|
return start + q1 + addPrefix( url ) + q2 + suffix;
|
||
|
|
||
|
}
|
||
|
|
||
|
function makeFDedQuotesModule( match, start, q1, url, q2 ) {
|
||
|
|
||
|
// modules require relative paths or fully qualified, otherwise they are module names
|
||
|
return `${start}${q1}${url.startsWith( '.' ) ? addPrefix( url ) : url}${q2}`;
|
||
|
|
||
|
}
|
||
|
|
||
|
function makeArrayLinksFDed( match, prefix, arrayStr, suffix ) {
|
||
|
|
||
|
const lines = arrayStr.split( ',' ).map( ( line ) => {
|
||
|
|
||
|
const m = arrayLineRE.exec( line );
|
||
|
return m
|
||
|
? `${m[ 1 ]}${addPrefix( m[ 2 ] )}${m[ 3 ]}`
|
||
|
: line;
|
||
|
|
||
|
} );
|
||
|
return `${prefix}${lines.join( ',' )}${suffix}`;
|
||
|
|
||
|
}
|
||
|
|
||
|
source = source.replace( srcRE, makeTaggedFDedQuotes );
|
||
|
source = source.replace( linkRE, makeTaggedFDedQuotes );
|
||
|
source = source.replace( imageSrcRE, makeTaggedFDedQuotes );
|
||
|
source = source.replace( urlPropRE, makeLinkFDedQuotes );
|
||
|
source = source.replace( loadFileRE, makeLinkFDedQuotes );
|
||
|
source = source.replace( loaderLoadRE, makeLinkFDedQuotes );
|
||
|
source = source.replace( workerRE, makeLinkFDedQuotes );
|
||
|
source = source.replace( importScriptsRE, makeLinkFDedQuotes );
|
||
|
source = source.replace( loaderArrayLoadRE, makeArrayLinksFDed );
|
||
|
source = source.replace( threejsUrlRE, makeTaggedFDedQuotes );
|
||
|
source = source.replace( moduleRE, makeFDedQuotesModule );
|
||
|
|
||
|
return source;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Called after parsing to give a change to update htmlParts
|
||
|
* @param {string} html The main page html turned into a template with the <style>, <script> and <body> parts extracted
|
||
|
* @param {Object<string, HTMLPart>} htmlParts All the extracted parts
|
||
|
* @return {string} The modified html template
|
||
|
*/
|
||
|
function extraHTMLParsing( html /* , htmlParts */ ) {
|
||
|
|
||
|
return html;
|
||
|
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Change JavaScript before uploading code to JSFiddle/Codepen
|
||
|
*
|
||
|
* @param {string} js JavaScript source
|
||
|
* @returns {string} The JavaScript source with any fixes applied.
|
||
|
*/
|
||
|
let version;
|
||
|
async function fixJSForCodeSite( js ) {
|
||
|
|
||
|
const moduleRE = /(import.*?)('|")(.*?)('|")/g;
|
||
|
|
||
|
// convert https://threejs.org/build/three.module.js -> https://cdn.jsdelivr.net/npm/three@<version>
|
||
|
// convert https://threejs.org/examples/jsm/.?? -> https://cdn.jsdelivr.net/npm/three@<version>/examples/jsm/.??
|
||
|
|
||
|
if ( ! version ) {
|
||
|
|
||
|
try {
|
||
|
|
||
|
const res = await fetch( 'https://raw.githubusercontent.com/mrdoob/three.js/master/package.json' );
|
||
|
const json = await res.json();
|
||
|
version = json.version;
|
||
|
|
||
|
} catch ( e ) {
|
||
|
|
||
|
console.error( e );
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
function addVersion( href ) {
|
||
|
|
||
|
if ( href.startsWith( window.location.origin ) ) {
|
||
|
|
||
|
if ( href.includes( '/build/three.module.js' ) ) {
|
||
|
|
||
|
return `https://cdn.jsdelivr.net/npm/three@${version}`;
|
||
|
|
||
|
} else if ( href.includes( '/examples/jsm/' ) ) {
|
||
|
|
||
|
const url = new URL( href );
|
||
|
return `https://cdn.jsdelivr.net/npm/three@${version}${url.pathname}${url.search}${url.hash}`;
|
||
|
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
return href;
|
||
|
|
||
|
}
|
||
|
|
||
|
function addVersionToURL( match, start, q1, url, q2 ) {
|
||
|
|
||
|
return start + q1 + addVersion( url ) + q2;
|
||
|
|
||
|
}
|
||
|
|
||
|
if ( version !== undefined ) {
|
||
|
|
||
|
js = js.replace( moduleRE, addVersionToURL );
|
||
|
|
||
|
}
|
||
|
|
||
|
return js;
|
||
|
|
||
|
}
|
||
|
|
||
|
window.lessonEditorSettings = {
|
||
|
extraHTMLParsing,
|
||
|
fixSourceLinks,
|
||
|
fixJSForCodeSite,
|
||
|
runOnResize: false,
|
||
|
lessonSettings: {
|
||
|
glDebug: false,
|
||
|
},
|
||
|
tags: [ 'three.js' ],
|
||
|
name: 'three.js',
|
||
|
icon: '/files/icon.svg',
|
||
|
};
|
||
|
|
||
|
}() );
|