mirror of
https://github.com/KwiTsukasa/kt-template-online-playground.git
synced 2026-05-28 00:47:42 +08:00
379 lines
11 KiB
HTML
379 lines
11 KiB
HTML
<!doctype html>
|
|
<html>
|
|
<head>
|
|
<style>
|
|
html.dark {
|
|
color-scheme: dark;
|
|
}
|
|
body {
|
|
font-family:
|
|
-apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu,
|
|
Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
|
|
}
|
|
</style>
|
|
<!-- PREVIEW-OPTIONS-HEAD-HTML -->
|
|
<script>
|
|
;(() => {
|
|
let scriptEls = []
|
|
|
|
window.process = { env: {} }
|
|
window.__modules__ = {}
|
|
|
|
window.__export__ = (mod, key, get) => {
|
|
Object.defineProperty(mod, key, {
|
|
enumerable: true,
|
|
configurable: true,
|
|
get,
|
|
})
|
|
}
|
|
|
|
window.__dynamic_import__ = (key) => {
|
|
return Promise.resolve(window.__modules__[key])
|
|
}
|
|
|
|
async function handle_message(ev) {
|
|
let { action, cmd_id } = ev.data
|
|
const send_message = (payload) =>
|
|
parent.postMessage({ ...payload }, ev.origin)
|
|
const send_reply = (payload) => send_message({ ...payload, cmd_id })
|
|
const send_ok = (response) =>
|
|
send_reply({ action: 'cmd_ok', args: response })
|
|
const send_error = (message, stack) =>
|
|
send_reply({ action: 'cmd_error', message, stack })
|
|
|
|
if (action === 'eval') {
|
|
try {
|
|
if (scriptEls.length) {
|
|
scriptEls.forEach((el) => {
|
|
document.head.removeChild(el)
|
|
})
|
|
scriptEls.length = 0
|
|
}
|
|
|
|
let { script: scripts } = ev.data.args
|
|
if (typeof scripts === 'string') scripts = [scripts]
|
|
|
|
for (const script of scripts) {
|
|
const scriptEl = document.createElement('script')
|
|
scriptEl.setAttribute('type', 'module')
|
|
// send ok in the module script to ensure sequential evaluation
|
|
// of multiple proxy.eval() calls
|
|
const done = new Promise((resolve) => {
|
|
window.__next__ = resolve
|
|
})
|
|
scriptEl.innerHTML = script + `\nwindow.__next__()`
|
|
document.head.appendChild(scriptEl)
|
|
scriptEl.onerror = (err) => send_error(err.message, err.stack)
|
|
scriptEls.push(scriptEl)
|
|
await done
|
|
}
|
|
if (window.__ssr_promise__) {
|
|
send_ok(await window.__ssr_promise__)
|
|
} else {
|
|
send_ok()
|
|
}
|
|
} catch (e) {
|
|
send_error(e.message, e.stack)
|
|
}
|
|
}
|
|
|
|
if (action === 'catch_clicks') {
|
|
try {
|
|
const top_origin = ev.origin
|
|
document.body.addEventListener('click', (event) => {
|
|
if (event.which !== 1) return
|
|
if (event.metaKey || event.ctrlKey || event.shiftKey) return
|
|
if (event.defaultPrevented) return
|
|
|
|
// ensure target is a link
|
|
let el = event.target
|
|
while (el && el.nodeName !== 'A') el = el.parentNode
|
|
if (!el || el.nodeName !== 'A') return
|
|
|
|
if (
|
|
el.hasAttribute('download') ||
|
|
el.getAttribute('rel') === 'external' ||
|
|
el.target ||
|
|
el.href.startsWith('javascript:') ||
|
|
!el.href
|
|
)
|
|
return
|
|
|
|
event.preventDefault()
|
|
|
|
if (el.href.startsWith(top_origin)) {
|
|
const url = new URL(el.href)
|
|
if (url.hash[0] === '#') {
|
|
window.location.hash = url.hash
|
|
return
|
|
}
|
|
}
|
|
|
|
window.open(el.href, '_blank')
|
|
})
|
|
send_ok()
|
|
} catch (e) {
|
|
send_error(e.message, e.stack)
|
|
}
|
|
}
|
|
}
|
|
|
|
window.addEventListener('message', handle_message, false)
|
|
|
|
window.onerror = function (msg, url, lineNo, columnNo, error) {
|
|
// ignore errors from import map polyfill - these are necessary for
|
|
// it to detect browser support
|
|
if (msg.includes('module specifier “vue”')) {
|
|
// firefox only error, ignore
|
|
return false
|
|
}
|
|
if (msg.includes("Module specifier, 'vue")) {
|
|
// Safari only
|
|
return false
|
|
}
|
|
try {
|
|
parent.postMessage({ action: 'error', value: error }, '*')
|
|
} catch (e) {
|
|
parent.postMessage({ action: 'error', value: msg }, '*')
|
|
}
|
|
}
|
|
|
|
window.addEventListener('unhandledrejection', (event) => {
|
|
if (
|
|
event.reason.message &&
|
|
event.reason.message.includes('Cross-origin')
|
|
) {
|
|
event.preventDefault()
|
|
return
|
|
}
|
|
try {
|
|
parent.postMessage(
|
|
{ action: 'unhandledrejection', value: event.reason },
|
|
'*',
|
|
)
|
|
} catch (e) {
|
|
parent.postMessage(
|
|
{ action: 'unhandledrejection', value: event.reason.message },
|
|
'*',
|
|
)
|
|
}
|
|
})
|
|
|
|
let previous = { level: null, args: null }
|
|
|
|
;['clear', 'log', 'info', 'dir', 'warn', 'error', 'table'].forEach(
|
|
(level) => {
|
|
const original = console[level]
|
|
console[level] = (...args) => {
|
|
const msg = args[0]
|
|
if (typeof msg === 'string') {
|
|
if (
|
|
msg.includes('You are running a development build of Vue') ||
|
|
msg.includes('You are running the esm-bundler build of Vue')
|
|
) {
|
|
return
|
|
}
|
|
}
|
|
|
|
original(...args)
|
|
|
|
const stringifiedArgs = stringify(args)
|
|
if (
|
|
previous.level === level &&
|
|
previous.args &&
|
|
previous.args === stringifiedArgs
|
|
) {
|
|
parent.postMessage(
|
|
{ action: 'console', level, duplicate: true },
|
|
'*',
|
|
)
|
|
} else {
|
|
previous = { level, args: stringifiedArgs }
|
|
|
|
try {
|
|
parent.postMessage({ action: 'console', level, args }, '*')
|
|
} catch (err) {
|
|
parent.postMessage(
|
|
{ action: 'console', level, args: args.map(toString) },
|
|
'*',
|
|
)
|
|
}
|
|
}
|
|
}
|
|
},
|
|
)
|
|
;[
|
|
{ method: 'group', action: 'console_group' },
|
|
{ method: 'groupEnd', action: 'console_group_end' },
|
|
{ method: 'groupCollapsed', action: 'console_group_collapsed' },
|
|
].forEach((group_action) => {
|
|
const original = console[group_action.method]
|
|
console[group_action.method] = (label) => {
|
|
parent.postMessage({ action: group_action.action, label }, '*')
|
|
|
|
original(label)
|
|
}
|
|
})
|
|
|
|
const timers = new Map()
|
|
const original_time = console.time
|
|
const original_timelog = console.timeLog
|
|
const original_timeend = console.timeEnd
|
|
|
|
console.time = (label = 'default') => {
|
|
original_time(label)
|
|
timers.set(label, performance.now())
|
|
}
|
|
console.timeLog = (label = 'default') => {
|
|
original_timelog(label)
|
|
const now = performance.now()
|
|
if (timers.has(label)) {
|
|
parent.postMessage(
|
|
{
|
|
action: 'console',
|
|
level: 'system-log',
|
|
args: [`${label}: ${now - timers.get(label)}ms`],
|
|
},
|
|
'*',
|
|
)
|
|
} else {
|
|
parent.postMessage(
|
|
{
|
|
action: 'console',
|
|
level: 'system-warn',
|
|
args: [`Timer '${label}' does not exist`],
|
|
},
|
|
'*',
|
|
)
|
|
}
|
|
}
|
|
console.timeEnd = (label = 'default') => {
|
|
original_timeend(label)
|
|
const now = performance.now()
|
|
if (timers.has(label)) {
|
|
parent.postMessage(
|
|
{
|
|
action: 'console',
|
|
level: 'system-log',
|
|
args: [`${label}: ${now - timers.get(label)}ms`],
|
|
},
|
|
'*',
|
|
)
|
|
} else {
|
|
parent.postMessage(
|
|
{
|
|
action: 'console',
|
|
level: 'system-warn',
|
|
args: [`Timer '${label}' does not exist`],
|
|
},
|
|
'*',
|
|
)
|
|
}
|
|
timers.delete(label)
|
|
}
|
|
|
|
const original_assert = console.assert
|
|
console.assert = (condition, ...args) => {
|
|
if (condition) {
|
|
const stack = new Error().stack
|
|
parent.postMessage(
|
|
{ action: 'console', level: 'assert', args, stack },
|
|
'*',
|
|
)
|
|
}
|
|
original_assert(condition, ...args)
|
|
}
|
|
|
|
const counter = new Map()
|
|
const original_count = console.count
|
|
const original_countreset = console.countReset
|
|
|
|
console.count = (label = 'default') => {
|
|
counter.set(label, (counter.get(label) || 0) + 1)
|
|
parent.postMessage(
|
|
{
|
|
action: 'console',
|
|
level: 'system-log',
|
|
args: `${label}: ${counter.get(label)}`,
|
|
},
|
|
'*',
|
|
)
|
|
original_count(label)
|
|
}
|
|
|
|
console.countReset = (label = 'default') => {
|
|
if (counter.has(label)) {
|
|
counter.set(label, 0)
|
|
} else {
|
|
parent.postMessage(
|
|
{
|
|
action: 'console',
|
|
level: 'system-warn',
|
|
args: `Count for '${label}' does not exist`,
|
|
},
|
|
'*',
|
|
)
|
|
}
|
|
original_countreset(label)
|
|
}
|
|
|
|
const original_trace = console.trace
|
|
|
|
console.trace = (...args) => {
|
|
const stack = new Error().stack
|
|
parent.postMessage(
|
|
{ action: 'console', level: 'trace', args, stack },
|
|
'*',
|
|
)
|
|
original_trace(...args)
|
|
}
|
|
|
|
function toString(value) {
|
|
if (value instanceof Error) {
|
|
return value.message
|
|
}
|
|
for (const fn of [
|
|
String,
|
|
(v) => Object.prototype.toString.call(v),
|
|
(v) => typeof v,
|
|
]) {
|
|
try {
|
|
return fn(value)
|
|
} catch (err) {}
|
|
}
|
|
}
|
|
|
|
function isComponentProxy(value) {
|
|
return (
|
|
value &&
|
|
typeof value === 'object' &&
|
|
value.__v_skip === true &&
|
|
typeof value.$nextTick === 'function' &&
|
|
value.$ &&
|
|
value._
|
|
)
|
|
}
|
|
|
|
function stringify(args) {
|
|
try {
|
|
return JSON.stringify(args, (key, value) => {
|
|
return isComponentProxy(value) ? '{component proxy}' : value
|
|
})
|
|
} catch (error) {
|
|
return null
|
|
}
|
|
}
|
|
})()
|
|
</script>
|
|
|
|
<!-- ES Module Shims: Import maps polyfill for modules browsers without import maps support (all except Chrome 89+) -->
|
|
<script async src="<!--ES-MODULE-SHIMS-LINK-->"></script>
|
|
<script type="importmap">
|
|
<!--IMPORT_MAP-->
|
|
</script>
|
|
</head>
|
|
<body>
|
|
<!--PREVIEW-OPTIONS-PLACEHOLDER-HTML-->
|
|
</body>
|
|
</html>
|