import * as React from "react" ; import { Split } from "./Split" ; import { Project , mimeTypeForFileType , SandboxRun } from "../models" ; import { logLn } from "../actions/AppActions" ; import appStore from "../stores/AppStore" ; interface SandboxWindow extends Window { /**
     * Creates an object URL to a Blob containing the file's data.
     */ getFileURL ( path ) : string ; } export class Sandbox extends React . Component < { } , { } > { container : HTMLDivElement ; private setContainer ( container ) { if ( container == null ) { return ; } this . container = container ; } onResizeBegin = ( ) => { this . container . style . pointerEvents = "none" ; } ; onResizeEnd = ( ) => { this . container . style . pointerEvents = "auto" ; } ; onSandboxRun = ( e ) => { this . run ( e . project , e . src ) ; } ; componentDidMount ( ) { Split . onResizeBegin . register ( this . onResizeBegin ) ; Split . onResizeEnd . register ( this . onResizeEnd ) ; appStore . onSandboxRun . register ( this . onSandboxRun ) ; } componentWillUnmount ( ) { Split . onResizeBegin . unregister ( this . onResizeBegin ) ; Split . onResizeEnd . unregister ( this . onResizeEnd ) ; appStore . onSandboxRun . unregister ( this . onSandboxRun ) ; } run ( project , src ) { const iframe = document . createElement ( "iframe" ) ; iframe . className = "sandbox" ; iframe . src = URL . createObjectURL ( new Blob ( [ src ] , { type : "text/html" } ) ) ; if ( this . container . firstChild ) { this . container . removeChild ( this . container . firstChild ) ; } this . container . appendChild ( iframe ) ; const contentWindow = iframe . contentWindow as SandboxWindow ; const logger = { logLn , } ; contentWindow . console . log = ( log => function ( ) { logger . logLn ( Array . prototype . join . call ( arguments ) ) ; log . apply ( contentWindow . console , arguments ) ; } ) ( contentWindow . console . log ) ; contentWindow . console . info = ( info => function ( ) { logger . logLn ( Array . prototype . join . call ( arguments ) , "info" ) ; info . apply ( contentWindow . console , arguments ) ; } ) ( contentWindow . console . info ) ; contentWindow . console . warn = ( warn => function ( ) { logger . logLn ( Array . prototype . join . call ( arguments ) , "warn" ) ; warn . apply ( contentWindow . console , arguments ) ; } ) ( contentWindow . console . warn ) ; contentWindow . console . error = ( error => function ( ) { logger . logLn ( Array . prototype . join . call ( arguments ) , "error" ) ; error . apply ( contentWindow . console , arguments ) ; } ) ( contentWindow . console . error ) ; contentWindow . fetch = async ( input , init ? ) => { const url = new URL ( input , "http://example.org/src/main.html" ) ; const file = project . getFile ( url . pathname . substr ( 1 ) ) ; if ( file ) { return Promise . resolve ( new Response ( file . getData ( ) , { status : 200 , statusText : "OK" , headers : { "Content-Type" : mimeTypeForFileType ( file . type ) } } ) ) ; } const response = await fetch ( input , init ) ; if ( response . status === 404 ) { return Promise . reject ( `Failed to fetch: ${ response . url }` ) ; } else { return Promise . resolve ( response ) ; } } ; contentWindow . getFileURL = ( path ) => { const file = project . getFile ( path ) ; if ( ! file ) { logger . logLn ( `Cannot find file ${ path }` , "error" ) ; return ; } const blob = new Blob ( [ file . getData ( ) ] , { type : mimeTypeForFileType ( file . type ) } ) ; return window . URL . createObjectURL ( blob ) ; } ; const ready = new Promise ( ( resolve ) => { ( iframe as any ) . onready = ( ) => { resolve ( contentWindow ) ; } ; } ) ; } render ( ) { return < div className = "fill" ref = { ( ref ) => this . setContainer ( ref ) } />; } }