( function () { // eslint-disable-line strict
'use strict'; // eslint-disable-line strict
/* global monaco, require, lessonEditorSettings */
const {
fixSourceLinks,
fixJSForCodeSite,
extraHTMLParsing,
runOnResize,
lessonSettings,
} = lessonEditorSettings;
const lessonHelperScriptRE = /' ) ||
getHTMLPart( inlineModuleScriptRE, obj, '' );
html = obj.html;
const fqURL = getFQUrl( url );
/** @type Object */
const scriptInfos = {};
g.rootScriptInfo = {
fqURL,
deps: [],
source: rootScript,
};
scriptInfos[ fqURL ] = g.rootScriptInfo;
const { text } = await getWorkerScripts( rootScript, fqURL, scriptInfos );
g.rootScriptInfo.source = text;
g.scriptInfos = scriptInfos;
for ( const [ fqURL, scriptInfo ] of Object.entries( scriptInfos ) ) {
addSource( 'js', basename( fqURL ), scriptInfo.source, scriptInfo );
}
const tm = titleRE.exec( html );
if ( tm ) {
g.title = tm[ 1 ];
}
const kScript = 'script';
const scripts = [];
html = html.replace( externalScriptRE, function ( p0, p1, p2, type, p3, p4 ) {
p1 = p1 || '';
scripts.push( `${p1}<${kScript} ${p2}${safeStr( type )}src="${p3}"${p4}>${kScript}>` );
return '';
} );
const prefix = getPrefix( url );
const rootPrefix = getRootPrefix( url );
function addCorrectPrefix( href ) {
return ( href.startsWith( '/' ) )
? `${rootPrefix}${href}`
: removeDotDotSlash( ( `${prefix}/${href}` ).replace( /\/.\//g, '/' ) );
}
function addPrefix( url ) {
return url.indexOf( '://' ) < 0 && ! url.startsWith( 'data:' ) && url[ 0 ] !== '?'
? removeDotDotSlash( addCorrectPrefix( url ) )
: url;
}
const importMapRE = /type\s*=["']importmap["']/;
const dataScripts = [];
html = html.replace( dataScriptRE, function ( p0, blockComments, scriptTagAttrs, content ) {
blockComments = blockComments || '';
if ( importMapRE.test( scriptTagAttrs ) ) {
const imap = JSON.parse( content );
const imports = imap.imports;
if ( imports ) {
for ( const [ k, url ] of Object.entries( imports ) ) {
if ( url.indexOf( '://' ) < 0 && ! url.startsWith( 'data:' ) ) {
imports[ k ] = addPrefix( url );
}
}
}
content = JSON.stringify( imap, null, '\t' );
}
dataScripts.push( `${blockComments}<${kScript} ${scriptTagAttrs}>${content}${kScript}>` );
return '';
} );
htmlParts.html.sources[ 0 ].source += dataScripts.join( '\n' );
htmlParts.html.sources[ 0 ].source += scripts.join( '\n' );
// add style section if there is non
if ( html.indexOf( '${css}' ) < 0 ) {
html = html.replace( '', '\n' );
}
// add hackedparams section.
// We need a way to pass parameters to a blob. Normally they'd be passed as
// query params but that only works in Firefox >:(
html = html.replace( '', '\n' );
html = extraHTMLParsing( html, htmlParts );
let links = '';
html = html.replace( cssLinkRE, function ( p0, p1 ) {
if ( isCSSLinkRE.test( p1 ) ) {
const m = hrefRE.exec( p1 );
if ( m ) {
links += `@import url("${m[ 1 ]}");\n`;
}
return '';
} else {
return p0;
}
} );
htmlParts.css.sources[ 0 ].source = links + htmlParts.css.sources[ 0 ].source;
g.html = html;
}
async function main() {
const query = getQuery();
g.url = getFQUrl( query.url );
g.query = getSearch( g.url );
let html;
try {
html = await getHTML( query.url );
} catch ( err ) {
console.log(err); // eslint-disable-line
return;
}
await parseHTML( query.url, html );
setupEditor();
if ( query.startPane ) {
const button = document.querySelector( '.button-' + query.startPane );
toggleSourcePane( button );
}
}
function getJavaScriptBlob( source ) {
const blob = new Blob( [ source ], { type: 'application/javascript' } );
return URL.createObjectURL( blob );
}
let blobGeneration = 0;
function makeBlobURLsForSources( scriptInfo ) {
++ blobGeneration;
function makeBlobURLForSourcesImpl( scriptInfo ) {
if ( scriptInfo.blobGenerationId !== blobGeneration ) {
scriptInfo.blobGenerationId = blobGeneration;
if ( scriptInfo.blobUrl ) {
URL.revokeObjectURL( scriptInfo.blobUrl );
}
scriptInfo.deps.forEach( makeBlobURLForSourcesImpl );
let text = scriptInfo.source;
scriptInfo.deps.forEach( ( depScriptInfo ) => {
text = text.split( depScriptInfo.fqURL ).join( depScriptInfo.blobUrl );
} );
scriptInfo.numLinesBeforeScript = 0;
if ( scriptInfo.isWorker ) {
const extra = `self.lessonSettings = ${JSON.stringify( lessonSettings )};
import '${dirname( scriptInfo.fqURL )}/resources/webgl-debug-helper.js';
import '${dirname( scriptInfo.fqURL )}/resources/lessons-worker-helper.js';`;
scriptInfo.numLinesBeforeScript = extra.split( '\n' ).length;
text = `${extra}\n${text}`;
}
scriptInfo.blobUrl = getJavaScriptBlob( text );
scriptInfo.munged = text;
}
}
makeBlobURLForSourcesImpl( scriptInfo );
}
function getSourceBlob( htmlParts ) {
g.rootScriptInfo.source = htmlParts.js;
makeBlobURLsForSources( g.rootScriptInfo );
const dname = dirname( g.url );
// HACK! for webgl-2d-vs... those examples are not in /webgl they're in /webgl/resources
// We basically assume url is https://foo/base/example.html so there will be 4 slashes
// If the path is longer than then we need '../' to back up so prefix works below
const prefix = dname; //`${dname}${dname.split('/').slice(4).map(() => '/..').join('')}`;
let source = g.html;
source = source.replace( '${hackedParams}', JSON.stringify( g.query ) );
source = source.replace( '${html}', htmlParts.html );
source = source.replace( '${css}', htmlParts.css );
source = source.replace( '${js}', g.rootScriptInfo.munged ); //htmlParts.js);
source = source.replace( '', `
` );
source = source.replace( '', `
` );
const scriptNdx = source.search( /\n`;
} ).join( '\n' );
const init = `
// ------
// Creates Blobs for the Scripts so things can be self contained for snippets/JSFiddle/Codepen
// even though they are using workers
//
(function() {
const idsToUrls = [];
const scriptElements = [...document.querySelectorAll('script[type=x-worker]')];
for (const scriptElement of scriptElements) {
let text = scriptElement.text;
for (const {id, url} of idsToUrls) {
text = text.split(id).join(url);
}
const blob = new Blob([text], {type: 'application/javascript'});
const url = URL.createObjectURL(blob);
const id = scriptElement.id;
idsToUrls.push({id, url});
}
window.getWorkerBlob = function() {
return idsToUrls.pop().url;
};
import(window.getWorkerBlob());
}());
`;
return {
js: init,
html,
};
}
async function fixHTMLForCodeSite( html ) {
html = html.replace( lessonHelperScriptRE, '' );
html = html.replace( webglDebugHelperScriptRE, '' );
return html;
}
async function openInCodepen() {
const comment = `// ${g.title}
// from ${g.url}
`;
getSourcesFromEditor();
const scripts = makeScriptsForWorkers( g.rootScriptInfo );
const code = await fixJSForCodeSite( scripts.js );
const html = await fixHTMLForCodeSite( htmlParts.html.sources[ 0 ].source );
const pen = {
title: g.title,
description: 'from: ' + g.url,
tags: lessonEditorSettings.tags,
editors: '101',
html: scripts.html + html,
css: htmlParts.css.sources[ 0 ].source,
js: comment + code,
};
const elem = document.createElement( 'div' );
elem.innerHTML = `
"
`;
elem.querySelector( 'input[name=data]' ).value = JSON.stringify( pen );
window.frameElement.ownerDocument.body.appendChild( elem );
elem.querySelector( 'form' ).submit();
window.frameElement.ownerDocument.body.removeChild( elem );
}
async function openInJSFiddle() {
const comment = `// ${g.title}
// from ${g.url}
`;
getSourcesFromEditor();
const scripts = makeScriptsForWorkers( g.rootScriptInfo );
const code = await fixJSForCodeSite( scripts.js );
const html = await fixHTMLForCodeSite( htmlParts.html.sources[ 0 ].source );
const elem = document.createElement( 'div' );
elem.innerHTML = `
`;
elem.querySelector( 'input[name=html]' ).value = scripts.html + html;
elem.querySelector( 'input[name=css]' ).value = htmlParts.css.sources[ 0 ].source;
elem.querySelector( 'input[name=js]' ).value = comment + code;
elem.querySelector( 'input[name=title]' ).value = g.title;
window.frameElement.ownerDocument.body.appendChild( elem );
elem.querySelector( 'form' ).submit();
window.frameElement.ownerDocument.body.removeChild( elem );
}
async function openInJSGist() {
const comment = `// ${g.title}
// from ${g.url}
`;
getSourcesFromEditor();
const scripts = makeScriptsForWorkers( g.rootScriptInfo );
const code = await fixJSForCodeSite( scripts.js );
const html = await fixHTMLForCodeSite( htmlParts.html.sources[ 0 ].source );
const gist = {
name: g.title,
settings: {},
files: [
{ name: 'index.html', content: scripts.html + html, },
{ name: 'index.css', content: htmlParts.css.sources[ 0 ].source, },
{ name: 'index.js', content: comment + code, },
],
};
window.open( 'https://jsgist.org/?newGist=true', '_blank' );
const send = ( e ) => {
e.source.postMessage( { type: 'newGist', data: gist }, '*' );
};
window.addEventListener( 'message', send, { once: true } );
}
/*
console.log();
h1 { color: red; }
foo
*/
function indent4( s ) {
return s.split( '\n' ).map( s => ` ${s}` ).join( '\n' );
}
async function openInStackOverflow() {
const comment = `// ${g.title}
// from ${g.url}
`;
getSourcesFromEditor();
const scripts = makeScriptsForWorkers( g.rootScriptInfo );
const code = await fixJSForCodeSite( scripts.js );
const html = await fixHTMLForCodeSite( htmlParts.html.sources[ 0 ].source );
const mainHTML = scripts.html + html;
const mainJS = comment + code;
const mainCSS = htmlParts.css.sources[ 0 ].source;
const asModule = /\bimport\b/.test( mainJS );
// Three.js wants us to use modules but Stack Overflow doesn't support them
const text = asModule
? `
${indent4( mainCSS )}
${indent4( mainHTML )}
`
: `
${indent4( mainJS )}
${indent4( mainCSS )}
${indent4( mainHTML )}
`;
const dialogElem = document.querySelector( '.copy-dialog' );
dialogElem.style.display = '';
const copyAreaElem = dialogElem.querySelector( '.copy-area' );
copyAreaElem.textContent = text;
const linkElem = dialogElem.querySelector( 'a' );
const tags = lessonEditorSettings.tags.filter( f => ! f.endsWith( '.org' ) ).join( ' ' );
linkElem.href = `https://stackoverflow.com/questions/ask?&tags=javascript ${tags}`;
}
function htmlTemplate( s ) {
return `
${s.title}
${s.body}
${s.script.startsWith( '<' )
? s.script
: `
`}
`;
}
// ---vvv---
// Copyright (c) 2013 Pieroxy
// This work is free. You can redistribute it and/or modify it
// under the terms of the WTFPL, Version 2
// For more information see LICENSE.txt or http://www.wtfpl.net/
//
// For more information, the home page:
// http://pieroxy.net/blog/pages/lz-string/testing.html
//
// LZ-based compression algorithm, version 1.4.4
//
// Modified:
// private property
const keyStrBase64 = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=';
function compressToBase64( input ) {
if ( input === null ) {
return '';
}
const res = _compress( input, 6, function ( a ) {
return keyStrBase64.charAt( a );
} );
switch ( res.length % 4 ) { // To produce valid Base64
default: // When could this happen ?
case 0 : return res;
case 1 : return res + '===';
case 2 : return res + '==';
case 3 : return res + '=';
}
}
function _compress( uncompressed, bitsPerChar, getCharFromInt ) {
let i;
let value;
const context_dictionary = {};
const context_dictionaryToCreate = {};
let context_c = '';
let context_wc = '';
let context_w = '';
let context_enlargeIn = 2; // Compensate for the first entry which should not count
let context_dictSize = 3;
let context_numBits = 2;
const context_data = [];
let context_data_val = 0;
let context_data_position = 0;
let ii;
for ( ii = 0; ii < uncompressed.length; ii += 1 ) {
context_c = uncompressed.charAt( ii );
if ( ! Object.prototype.hasOwnProperty.call( context_dictionary, context_c ) ) {
context_dictionary[ context_c ] = context_dictSize ++;
context_dictionaryToCreate[ context_c ] = true;
}
context_wc = context_w + context_c;
if ( Object.prototype.hasOwnProperty.call( context_dictionary, context_wc ) ) {
context_w = context_wc;
} else {
if ( Object.prototype.hasOwnProperty.call( context_dictionaryToCreate, context_w ) ) {
if ( context_w.charCodeAt( 0 ) < 256 ) {
for ( i = 0; i < context_numBits; i ++ ) {
context_data_val = ( context_data_val << 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
}
value = context_w.charCodeAt( 0 );
for ( i = 0; i < 8; i ++ ) {
context_data_val = ( context_data_val << 1 ) | ( value & 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = value >> 1;
}
} else {
value = 1;
for ( i = 0; i < context_numBits; i ++ ) {
context_data_val = ( context_data_val << 1 ) | value;
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = 0;
}
value = context_w.charCodeAt( 0 );
for ( i = 0; i < 16; i ++ ) {
context_data_val = ( context_data_val << 1 ) | ( value & 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = value >> 1;
}
}
context_enlargeIn --;
if ( context_enlargeIn === 0 ) {
context_enlargeIn = Math.pow( 2, context_numBits );
context_numBits ++;
}
delete context_dictionaryToCreate[ context_w ];
} else {
value = context_dictionary[ context_w ];
for ( i = 0; i < context_numBits; i ++ ) {
context_data_val = ( context_data_val << 1 ) | ( value & 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = value >> 1;
}
}
context_enlargeIn --;
if ( context_enlargeIn === 0 ) {
context_enlargeIn = Math.pow( 2, context_numBits );
context_numBits ++;
}
// Add wc to the dictionary.
context_dictionary[ context_wc ] = context_dictSize ++;
context_w = String( context_c );
}
}
// Output the code for w.
if ( context_w !== '' ) {
if ( Object.prototype.hasOwnProperty.call( context_dictionaryToCreate, context_w ) ) {
if ( context_w.charCodeAt( 0 ) < 256 ) {
for ( i = 0; i < context_numBits; i ++ ) {
context_data_val = ( context_data_val << 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
}
value = context_w.charCodeAt( 0 );
for ( i = 0; i < 8; i ++ ) {
context_data_val = ( context_data_val << 1 ) | ( value & 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = value >> 1;
}
} else {
value = 1;
for ( i = 0; i < context_numBits; i ++ ) {
context_data_val = ( context_data_val << 1 ) | value;
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = 0;
}
value = context_w.charCodeAt( 0 );
for ( i = 0; i < 16; i ++ ) {
context_data_val = ( context_data_val << 1 ) | ( value & 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = value >> 1;
}
}
context_enlargeIn --;
if ( context_enlargeIn === 0 ) {
context_enlargeIn = Math.pow( 2, context_numBits );
context_numBits ++;
}
delete context_dictionaryToCreate[ context_w ];
} else {
value = context_dictionary[ context_w ];
for ( i = 0; i < context_numBits; i ++ ) {
context_data_val = ( context_data_val << 1 ) | ( value & 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = value >> 1;
}
}
context_enlargeIn --;
if ( context_enlargeIn === 0 ) {
context_numBits ++;
}
}
// Mark the end of the stream
value = 2;
for ( i = 0; i < context_numBits; i ++ ) {
context_data_val = ( context_data_val << 1 ) | ( value & 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data_position = 0;
context_data.push( getCharFromInt( context_data_val ) );
context_data_val = 0;
} else {
context_data_position ++;
}
value = value >> 1;
}
// Flush the last char
for ( ;; ) {
context_data_val = ( context_data_val << 1 );
if ( context_data_position === bitsPerChar - 1 ) {
context_data.push( getCharFromInt( context_data_val ) );
break;
} else {
context_data_position ++;
}
}
return context_data.join( '' );
}
function compress( input ) {
return compressToBase64( input )
.replace( /\+/g, '-' ) // Convert '+' to '-'
.replace( /\//g, '_' ) // Convert '/' to '_'
.replace( /=+$/, '' ); // Remove ending '='
}
function getParameters( parameters ) {
return compress( JSON.stringify( parameters ) );
}
// -- ^^^ ---
async function openInCodeSandbox() {
const comment = `// ${g.title}
// from ${g.url}
`;
getSourcesFromEditor();
const scripts = getScripts( g.rootScriptInfo );
const mainScript = scripts.pop();
const code = await fixJSForCodeSite( mainScript.text );
const html = await fixHTMLForCodeSite( htmlParts.html.sources[ 0 ].source );
const names = scripts.map( s => s.name );
const files = scripts.reduce( ( files, { name, text: content } ) => {
files[ name ] = { content };
return files;
}, {
'index.html': {
content: htmlTemplate( {
body: html,
css: htmlParts.css.sources[ 0 ].source,
title: g.title,
script: comment + code,
} ),
},
'sandbox.config.json': {
content: '{\n "template": "static"\n}\n',
},
'package.json': {
content: JSON.stringify( {
'name': 'static',
'version': '1.0.0',
'description': 'This is a static template with no bundling',
'main': 'index.html',
'scripts': {
'start': 'serve',
'build': 'echo This is a static template, there is no bundler or bundling involved!',
},
'license': 'MIT',
'devDependencies': {
'serve': '^11.2.0',
},
}, null, 2 ),
},
} );
for ( const file of Object.values( files ) ) {
for ( const name of names ) {
file.content = file.content.split( name ).join( `./${name}` );
}
}
const parameters = getParameters( { files } );
const elem = document.createElement( 'div' );
elem.innerHTML = `
`;
elem.querySelector( 'input[name=parameters]' ).value = parameters;
window.frameElement.ownerDocument.body.appendChild( elem );
elem.querySelector( 'form' ).submit();
window.frameElement.ownerDocument.body.removeChild( elem );
}
/*
async function openInStackBlitz() {
const comment = `// ${g.title}
// from ${g.url}
`;
getSourcesFromEditor();
const scripts = getScripts(g.rootScriptInfo);
const code = await fixJSForCodeSite(scripts.js);
const html = await fixHTMLForCodeSite(htmlParts.html.sources[0].source);
const mainScript = scripts.pop();
const names = scripts.map(s => s.name);
const files = scripts.reduce((files, {name, text: content}) => {
files[name] = {content};
return files;
}, {
'index.html': {
content: htmlTemplate({
body: html,
css: htmlParts.css.sources[0].source,
title: g.title,
script: '',
}),
},
'index.js': {
content: comment + code,
},
// "tsconfig.json": {
// content: JSON.stringify({
// "compilerOptions": {
// "target": "esnext"
// }
// }, null, 2),
// },
'package.json': {
content: JSON.stringify({
'name': 'js',
'version': '0.0.0',
'private': true,
'dependencies': {}
}, null, 2),
}
});
const elem = document.createElement('div');
elem.innerHTML = `
`;
const form = elem.querySelector('form');
for (const [name, file] of Object.entries(files)) {
for (const name of names) {
file.content = file.content.split(name).join(`./${name}`);
}
const input = document.createElement('input');
input.type = 'hidden';
input.name = `project[files][${name}]`;
input.value = file.content;
form.appendChild(input);
}
window.frameElement.ownerDocument.body.appendChild(elem);
form.submit();
window.frameElement.ownerDocument.body.removeChild(elem);
}
*/
document.querySelectorAll( '.dialog' ).forEach( dialogElem => {
dialogElem.addEventListener( 'click', function ( e ) {
if ( e.target === this ) {
this.style.display = 'none';
}
} );
dialogElem.addEventListener( 'keydown', function ( e ) {
console.log( e.keyCode );
if ( e.keyCode === 27 ) {
this.style.display = 'none';
}
} );
} );
const exportDialogElem = document.querySelector( '.export' );
function openExport() {
exportDialogElem.style.display = '';
exportDialogElem.firstElementChild.focus();
}
function closeExport( fn ) {
return () => {
exportDialogElem.style.display = 'none';
fn();
};
}
document.querySelector( '.button-export' ).addEventListener( 'click', openExport );
function selectFile( info, ndx, fileDivs ) {
if ( info.editors.length <= 1 ) {
return;
}
info.editors.forEach( ( editorInfo, i ) => {
const selected = i === ndx;
editorInfo.div.style.display = selected ? '' : 'none';
editorInfo.editor.layout();
addRemoveClass( fileDivs.children[ i ], 'fileSelected', selected );
} );
}
function showEditorSubPane( type, ndx ) {
const info = htmlParts[ type ];
selectFile( info, ndx, info.files );
}
function setupEditor() {
forEachHTMLPart( function ( info, ndx, name ) {
info.pane = document.querySelector( '.panes>.' + name );
info.code = info.pane.querySelector( '.code' );
info.files = info.pane.querySelector( '.files' );
info.editors = info.sources.map( ( sourceInfo, ndx ) => {
if ( info.sources.length > 1 ) {
const div = document.createElement( 'div' );
div.textContent = basename( sourceInfo.name );
info.files.appendChild( div );
div.addEventListener( 'click', () => {
selectFile( info, ndx, info.files );
} );
}
const div = document.createElement( 'div' );
info.code.appendChild( div );
const editor = runEditor( div, sourceInfo.source, info.language );
sourceInfo.editor = editor;
return {
div,
editor,
};
} );
info.button = document.querySelector( '.button-' + name );
info.button.addEventListener( 'click', function () {
toggleSourcePane( info.button );
runIfNeeded();
} );
} );
g.fullscreen = document.querySelector( '.button-fullscreen' );
g.fullscreen.addEventListener( 'click', toggleFullscreen );
g.run = document.querySelector( '.button-run' );
g.run.addEventListener( 'click', run );
g.iframe = document.querySelector( '.result>iframe' );
g.other = document.querySelector( '.panes .other' );
document.querySelector( '.button-codepen' ).addEventListener( 'click', closeExport( openInCodepen ) );
document.querySelector( '.button-jsfiddle' ).addEventListener( 'click', closeExport( openInJSFiddle ) );
document.querySelector( '.button-jsgist' ).addEventListener( 'click', closeExport( openInJSGist ) );
document.querySelector( '.button-stackoverflow' ).addEventListener( 'click', closeExport( openInStackOverflow ) );
document.querySelector( '.button-codesandbox' ).addEventListener( 'click', closeExport( openInCodeSandbox ) );
//document.querySelector('.button-stackblitz').addEventListener('click', openInStackBlitz);
g.result = document.querySelector( '.panes .result' );
g.resultButton = document.querySelector( '.button-result' );
g.resultButton.addEventListener( 'click', function () {
toggleResultPane();
runIfNeeded();
} );
g.result.style.display = 'none';
toggleResultPane();
if ( window.innerWidth >= 1000 ) {
toggleSourcePane( htmlParts.js.button );
}
window.addEventListener( 'resize', resize );
showEditorSubPane( 'js', 0 );
showOtherIfAllPanesOff();
document.querySelector( '.other .loading' ).style.display = 'none';
resize();
run();
}
function toggleFullscreen() {
try {
toggleIFrameFullscreen( window );
resize();
runIfNeeded();
} catch ( e ) {
console.error(e); // eslint-disable-line
}
}
function runIfNeeded() {
if ( runOnResize ) {
run();
}
}
function run() {
g.setPosition = false;
const url = getSourceBlobFromEditor();
// g.iframe.src = url;
// work around firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=1828286
g.iframe.contentWindow.location.replace(url);
}
function addClass( elem, className ) {
const parts = elem.className.split( ' ' );
if ( parts.indexOf( className ) < 0 ) {
elem.className = elem.className + ' ' + className;
}
}
function removeClass( elem, className ) {
const parts = elem.className.split( ' ' );
const numParts = parts.length;
for ( ;; ) {
const ndx = parts.indexOf( className );
if ( ndx < 0 ) {
break;
}
parts.splice( ndx, 1 );
}
if ( parts.length !== numParts ) {
elem.className = parts.join( ' ' );
return true;
}
return false;
}
function toggleClass( elem, className ) {
if ( removeClass( elem, className ) ) {
return false;
} else {
addClass( elem, className );
return true;
}
}
function toggleIFrameFullscreen( childWindow ) {
const frame = childWindow.frameElement;
if ( frame ) {
const isFullScreen = toggleClass( frame, 'fullscreen' );
frame.ownerDocument.body.style.overflow = isFullScreen ? 'hidden' : '';
}
}
function addRemoveClass( elem, className, add ) {
if ( add ) {
addClass( elem, className );
} else {
removeClass( elem, className );
}
}
function toggleSourcePane( pressedButton ) {
forEachHTMLPart( function ( info ) {
const pressed = pressedButton === info.button;
if ( pressed && ! info.showing ) {
addClass( info.button, 'show' );
info.pane.style.display = 'flex';
info.showing = true;
} else {
removeClass( info.button, 'show' );
info.pane.style.display = 'none';
info.showing = false;
}
} );
showOtherIfAllPanesOff();
resize();
}
function showingResultPane() {
return g.result.style.display !== 'none';
}
function toggleResultPane() {
const showing = showingResultPane();
g.result.style.display = showing ? 'none' : 'block';
addRemoveClass( g.resultButton, 'show', ! showing );
showOtherIfAllPanesOff();
resize();
}
function showOtherIfAllPanesOff() {
let paneOn = showingResultPane();
forEachHTMLPart( function ( info ) {
paneOn = paneOn || info.showing;
} );
g.other.style.display = paneOn ? 'none' : 'block';
}
// seems like we should probably store a map
function getEditorNdxByBlobUrl( type, url ) {
return htmlParts[ type ].sources.findIndex( source => source.scriptInfo.blobUrl === url );
}
function getActualLineNumberAndMoveTo( url, lineNo, colNo ) {
let origUrl = url;
let actualLineNo = lineNo;
const scriptInfo = Object.values( g.scriptInfos ).find( scriptInfo => scriptInfo.blobUrl === url );
if ( scriptInfo ) {
actualLineNo = lineNo - scriptInfo.numLinesBeforeScript;
origUrl = basename( scriptInfo.fqURL );
if ( ! g.setPosition ) {
// Only set the first position
g.setPosition = true;
const editorNdx = getEditorNdxByBlobUrl( 'js', url );
if ( editorNdx >= 0 ) {
showEditorSubPane( 'js', editorNdx );
const editor = htmlParts.js.editors[ editorNdx ].editor;
editor.setPosition( {
lineNumber: actualLineNo,
column: colNo,
} );
editor.revealLineInCenterIfOutsideViewport( actualLineNo );
editor.focus();
}
}
}
return { origUrl, actualLineNo };
}
window.getActualLineNumberAndMoveTo = getActualLineNumberAndMoveTo;
function runEditor( parent, source, language ) {
return monaco.editor.create( parent, {
value: source,
language: language,
//lineNumbers: false,
theme: 'vs-dark',
disableTranslate3d: true,
// model: null,
scrollBeyondLastLine: false,
minimap: { enabled: false },
} );
}
async function runAsBlob() {
const query = getQuery();
g.url = getFQUrl( query.url );
g.query = getSearch( g.url );
let html;
try {
html = await getHTML( query.url );
} catch ( err ) {
console.log(err); // eslint-disable-line
return;
}
await parseHTML( query.url, html );
window.location.href = getSourceBlobFromOrig();
}
function applySubstitutions() {
[ ...document.querySelectorAll( '[data-subst]' ) ].forEach( ( elem ) => {
elem.dataset.subst.split( '&' ).forEach( ( pair ) => {
const [ attr, key ] = pair.split( '|' );
elem[ attr ] = lessonEditorSettings[ key ];
} );
} );
}
function start() {
const parentQuery = getQuery( window.parent.location.search );
const isSmallish = window.navigator.userAgent.match( /Android|iPhone|iPod|Windows Phone/i );
const isEdge = window.navigator.userAgent.match( /Edge/i );
if ( isEdge || isSmallish || parentQuery.editor === 'false' ) {
runAsBlob();
// var url = query.url;
// window.location.href = url;
} else {
applySubstitutions();
require.config( { paths: { 'vs': 'https://cdn.jsdelivr.net/npm/monaco-editor@0.34.1/min/vs' } } );
require( [ 'vs/editor/editor.main' ], main );
}
}
start();
}() );