(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}>`); 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 (let [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}`); 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(//); g.rootScriptInfo.numLinesBeforeScript = (source.substring(0, scriptNdx).match(/\n/g) || []).length; const blob = new Blob([source], {type: 'text/html'}); // This seems hacky. We are combining html/css/js into one html blob but we already made // a blob for the JS so let's replace that blob. That means it will get auto-released when script blobs // are regenerated. It also means error reporting will work const blobUrl = URL.createObjectURL(blob); URL.revokeObjectURL(g.rootScriptInfo.blobUrl); g.rootScriptInfo.blobUrl = blobUrl; return blobUrl; } function getSourcesFromEditor() { for (const partTypeInfo of Object.values(htmlParts)) { for (const source of partTypeInfo.sources) { source.source = source.editor.getValue(); // hack: shouldn't store this twice. Also see other comment, // should consolidate so scriptInfo is used for css and html if (source.scriptInfo) { source.scriptInfo.source = source.source; } } } } function getSourceBlobFromEditor() { getSourcesFromEditor(); return getSourceBlob({ html: htmlParts.html.sources[0].source, css: htmlParts.css.sources[0].source, js: htmlParts.js.sources[0].source, }); } function getSourceBlobFromOrig() { return getSourceBlob({ html: htmlParts.html.sources[0].source, css: htmlParts.css.sources[0].source, js: htmlParts.js.sources[0].source, }); } function dirname(path) { const ndx = path.lastIndexOf('/'); return path.substring(0, ndx); } function basename(path) { const ndx = path.lastIndexOf('/'); return path.substring(ndx + 1); } function resize() { forEachHTMLPart(function(info) { info.editors.forEach((editorInfo) => { editorInfo.editor.layout(); }); }); } function getScripts(scriptInfo) { ++blobGeneration; function getScriptsImpl(scriptInfo) { const scripts = []; if (scriptInfo.blobGenerationId !== blobGeneration) { scriptInfo.blobGenerationId = blobGeneration; scripts.push(...scriptInfo.deps.map(getScriptsImpl).flat()); let text = scriptInfo.source; scriptInfo.deps.forEach((depScriptInfo) => { text = text.split(depScriptInfo.fqURL).join(`worker-${basename(depScriptInfo.fqURL)}`); }); scripts.push({ name: `worker-${basename(scriptInfo.fqURL)}`, text, }); } return scripts; } return getScriptsImpl(scriptInfo); } function makeScriptsForWorkers(scriptInfo) { const scripts = getScripts(scriptInfo); if (scripts.length === 1) { return { js: scripts[0].text, html: '', }; } // scripts[last] = main script // scripts[last - 1] = worker const mainScriptInfo = scripts[scripts.length - 1]; const workerScriptInfo = scripts[scripts.length - 2]; const workerName = workerScriptInfo.name; mainScriptInfo.text = mainScriptInfo.text.split(`'${workerName}'`).join('getWorkerBlob()'); const html = scripts.map((nameText) => { const {name, text} = nameText; return `\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; } 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': '/manual/3rdparty/monaco-editor/min/vs' }}); require(['vs/editor/editor.main'], main); } } start(); }());