Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions client/custom.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,41 @@ declare module '*.svg' {
export default ReactComponent;
}

declare module 'blob-util' {
export function createBlob(parts: any[], options?: { type: string }): Blob;
export function createObjectURL(blob: Blob): string;
export function revokeObjectURL(url: string): void;
}

declare module 'mime' {
export function getType(path: string): string | null;
}

declare module 'loop-protect' {
function loopProtect(code: string): string;
export = loopProtect;
}

declare module 'jshint' {
export const JSHINT: {
(code: string): boolean;
errors: Array<{
line: number;
character: number;
reason: string;
code: string;
}>;
};
}

declare module 'decomment' {
function decomment(
code: string,
options?: { ignore?: RegExp; space?: boolean }
): string;
export = decomment;
}

// Extend window for Redux DevTools
interface Window {
__REDUX_DEVTOOLS_EXTENSION__?: () => any;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import blobUtil from 'blob-util';
import PropTypes from 'prop-types';
import React, { useRef, useEffect, useMemo } from 'react';
import styled from 'styled-components';
import loopProtect from 'loop-protect';
Expand All @@ -17,33 +16,45 @@ import {
import { getAllScriptOffsets } from '../../utils/consoleUtils';
import { registerFrame } from '../../utils/dispatcher';
import { createBlobUrl } from './filesReducer';
import type { File } from './filesReducer';
import resolvePathsForElementsWithAttribute from '../../../server/utils/resolveUtils';

let objectUrls = {};
let objectPaths = {};
interface ObjectUrls {
[key: string]: string;
}

interface ObjectPaths {
[key: string]: string;
}

let objectUrls: ObjectUrls = {};
let objectPaths: ObjectPaths = {};

interface FrameProps {
fullView?: boolean;
}

const Frame = styled.iframe`
const Frame = styled.iframe<FrameProps>`
min-height: 100%;
min-width: 100%;
position: absolute;
border-width: 0;
${({ fullView }) =>
${({ fullView }: FrameProps) =>
fullView &&
`
position: relative;
`}
`;

function resolveCSSLinksInString(content, files) {
function resolveCSSLinksInString(content: string, files: File[]): string {
let newContent = content;
let cssFileStrings = content.match(STRING_REGEX);
cssFileStrings = cssFileStrings || [];
const cssFileStrings = content.match(STRING_REGEX) || [];
cssFileStrings.forEach((cssFileString) => {
if (cssFileString.match(MEDIA_FILE_QUOTED_REGEX)) {
const filePath = cssFileString.substr(1, cssFileString.length - 2);
const quoteCharacter = cssFileString.substr(0, 1);
const resolvedFile = resolvePathToFile(filePath, files);
if (resolvedFile) {
const resolvedFile = resolvePathToFile(filePath, files) as File | false;
if (resolvedFile && typeof resolvedFile === 'object') {
if (resolvedFile.url) {
newContent = newContent.replace(
cssFileString,
Expand All @@ -56,7 +67,7 @@ function resolveCSSLinksInString(content, files) {
return newContent;
}

function jsPreprocess(jsText) {
function jsPreprocess(jsText: string): string {
let newContent = jsText;
// check the code for js errors before sending it to strip comments
// or loops.
Expand All @@ -72,17 +83,16 @@ function jsPreprocess(jsText) {
return newContent;
}

function resolveJSLinksInString(content, files) {
function resolveJSLinksInString(content: string, files: File[]): string {
let newContent = content;
let jsFileStrings = content.match(STRING_REGEX);
jsFileStrings = jsFileStrings || [];
const jsFileStrings = content.match(STRING_REGEX) || [];
jsFileStrings.forEach((jsFileString) => {
if (jsFileString.match(MEDIA_FILE_QUOTED_REGEX)) {
const filePath = jsFileString.substr(1, jsFileString.length - 2);
const quoteCharacter = jsFileString.substr(0, 1);
const resolvedFile = resolvePathToFile(filePath, files);
const resolvedFile = resolvePathToFile(filePath, files) as File | false;

if (resolvedFile) {
if (resolvedFile && typeof resolvedFile === 'object') {
if (resolvedFile.url) {
newContent = newContent.replace(
jsFileString,
Expand All @@ -101,16 +111,19 @@ function resolveJSLinksInString(content, files) {
return jsPreprocess(newContent);
}

function resolveScripts(sketchDoc, files) {
function resolveScripts(sketchDoc: Document, files: File[]): void {
const scriptsInHTML = sketchDoc.getElementsByTagName('script');
const scriptsInHTMLArray = Array.prototype.slice.call(scriptsInHTML);
scriptsInHTMLArray.forEach((script) => {
scriptsInHTMLArray.forEach((script: HTMLScriptElement) => {
if (
script.getAttribute('src') &&
script.getAttribute('src').match(NOT_EXTERNAL_LINK_REGEX) !== null
script.getAttribute('src')!.match(NOT_EXTERNAL_LINK_REGEX) !== null
) {
const resolvedFile = resolvePathToFile(script.getAttribute('src'), files);
if (resolvedFile) {
const resolvedFile = resolvePathToFile(
script.getAttribute('src')!,
files
) as File | false;
if (resolvedFile && typeof resolvedFile === 'object') {
if (resolvedFile.url) {
script.setAttribute('src', resolvedFile.url);
} else {
Expand All @@ -122,8 +135,10 @@ function resolveScripts(sketchDoc, files) {
// objectUrls[blobUrl] = `${resolvedFile.filePath}${
// resolvedFile.filePath.length > 0 ? '/' : ''
// }${resolvedFile.name}`;
objectUrls[blobUrl] = `${resolvedFile.filePath}/${resolvedFile.name}`;
objectPaths[blobPath] = resolvedFile.name;
objectUrls[blobUrl] = `${resolvedFile.filePath || ''}/${
resolvedFile.name
}`;
objectPaths[blobPath!] = resolvedFile.name;
// script.setAttribute('data-tag', `${startTag}${resolvedFile.name}`);
// script.removeAttribute('src');
// script.innerHTML = resolvedFile.content; // eslint-disable-line
Expand All @@ -132,7 +147,7 @@ function resolveScripts(sketchDoc, files) {
} else if (
!(
script.getAttribute('src') &&
script.getAttribute('src').match(EXTERNAL_LINK_REGEX)
script.getAttribute('src')!.match(EXTERNAL_LINK_REGEX)
) !== null
) {
script.setAttribute('crossorigin', '');
Expand All @@ -141,37 +156,40 @@ function resolveScripts(sketchDoc, files) {
});
}

function resolveStyles(sketchDoc, files) {
function resolveStyles(sketchDoc: Document, files: File[]): void {
const inlineCSSInHTML = sketchDoc.getElementsByTagName('style');
const inlineCSSInHTMLArray = Array.prototype.slice.call(inlineCSSInHTML);
inlineCSSInHTMLArray.forEach((style) => {
inlineCSSInHTMLArray.forEach((style: HTMLStyleElement) => {
style.innerHTML = resolveCSSLinksInString(style.innerHTML, files); // eslint-disable-line
});

const cssLinksInHTML = sketchDoc.querySelectorAll('link[rel="stylesheet"]');
const cssLinksInHTMLArray = Array.prototype.slice.call(cssLinksInHTML);
cssLinksInHTMLArray.forEach((css) => {
cssLinksInHTMLArray.forEach((css: HTMLLinkElement) => {
if (
css.getAttribute('href') &&
css.getAttribute('href').match(NOT_EXTERNAL_LINK_REGEX) !== null
css.getAttribute('href')!.match(NOT_EXTERNAL_LINK_REGEX) !== null
) {
const resolvedFile = resolvePathToFile(css.getAttribute('href'), files);
if (resolvedFile) {
const resolvedFile = resolvePathToFile(
css.getAttribute('href')!,
files
) as File | false;
if (resolvedFile && typeof resolvedFile === 'object') {
if (resolvedFile.url) {
css.href = resolvedFile.url; // eslint-disable-line
} else {
const style = sketchDoc.createElement('style');
style.innerHTML = `\n${resolvedFile.content}`;
sketchDoc.head.appendChild(style);
css.parentElement.removeChild(css);
css.parentElement!.removeChild(css);
}
}
}
});
}

function resolveJSAndCSSLinks(files) {
const newFiles = [];
function resolveJSAndCSSLinks(files: File[]): File[] {
const newFiles: File[] = [];
files.forEach((file) => {
const newFile = { ...file };
if (file.name.match(/.*\.js$/i)) {
Expand All @@ -184,25 +202,35 @@ function resolveJSAndCSSLinks(files) {
return newFiles;
}

function addLoopProtect(sketchDoc) {
function addLoopProtect(sketchDoc: Document): void {
const scriptsInHTML = sketchDoc.getElementsByTagName('script');
const scriptsInHTMLArray = Array.prototype.slice.call(scriptsInHTML);
scriptsInHTMLArray.forEach((script) => {
scriptsInHTMLArray.forEach((script: HTMLScriptElement) => {
script.innerHTML = jsPreprocess(script.innerHTML); // eslint-disable-line
});
}

function injectLocalFiles(files, htmlFile, options) {
interface InjectOptions {
basePath: string;
gridOutput: boolean;
textOutput: boolean;
}

function injectLocalFiles(
files: File[],
htmlFile: File,
options: InjectOptions
): string {
const { basePath, gridOutput, textOutput } = options;
let scriptOffs = [];
let scriptOffs: any[] = [];
objectUrls = {};
objectPaths = {};
const resolvedFiles = resolveJSAndCSSLinks(files);
const parser = new DOMParser();
const sketchDoc = parser.parseFromString(htmlFile.content, 'text/html');

const base = sketchDoc.createElement('base');
base.href = `${window.origin}${basePath}${basePath.length > 1 && '/'}`;
base.href = `${window.origin}${basePath}${basePath.length > 1 ? '/' : ''}`;
sketchDoc.head.appendChild(base);

resolvePathsForElementsWithAttribute('src', sketchDoc, resolvedFiles);
Expand Down Expand Up @@ -253,18 +281,32 @@ p5.prototype.registerMethod('afterSetup', p5.prototype.ensureAccessibleCanvas);`
return `<!DOCTYPE HTML>\n${sketchDoc.documentElement.outerHTML}`;
}

function getHtmlFile(files) {
function getHtmlFile(files: File[]): File {
return files.filter((file) => file.name.match(/.*\.html$/i))[0];
}

function EmbedFrame({ files, isPlaying, basePath, gridOutput, textOutput }) {
const iframe = useRef();
interface EmbedFrameProps {
files: File[];
isPlaying: boolean;
basePath: string;
gridOutput: boolean;
textOutput: boolean;
}

function EmbedFrame({
files,
isPlaying,
basePath,
gridOutput,
textOutput
}: EmbedFrameProps) {
const iframe = useRef<HTMLIFrameElement>(null);
const htmlFile = useMemo(() => getHtmlFile(files), [files]);
const srcRef = useRef();
const srcRef = useRef<string>();

useEffect(() => {
const unsubscribe = registerFrame(
iframe.current.contentWindow,
iframe.current!.contentWindow!,
window.origin
);
return () => {
Expand All @@ -273,16 +315,20 @@ function EmbedFrame({ files, isPlaying, basePath, gridOutput, textOutput }) {
});

function renderSketch() {
const doc = iframe.current;
const doc = iframe.current!;
if (isPlaying) {
const htmlDoc = injectLocalFiles(files, htmlFile, {
basePath,
gridOutput,
textOutput
});
const generatedHtmlFile = {
const generatedHtmlFile: File = {
id: 'generated',
_id: 'generated',
name: 'index.html',
content: htmlDoc
content: htmlDoc,
fileType: 'file',
children: []
};
const htmlUrl = createBlobUrl(generatedHtmlFile);
const toRevoke = srcRef.current;
Expand Down Expand Up @@ -310,18 +356,5 @@ function EmbedFrame({ files, isPlaying, basePath, gridOutput, textOutput }) {
);
}

EmbedFrame.propTypes = {
files: PropTypes.arrayOf(
PropTypes.shape({
id: PropTypes.string.isRequired,
name: PropTypes.string.isRequired,
content: PropTypes.string.isRequired
})
).isRequired,
isPlaying: PropTypes.bool.isRequired,
basePath: PropTypes.string.isRequired,
gridOutput: PropTypes.bool.isRequired,
textOutput: PropTypes.bool.isRequired
};

// eslint-disable-next-line import/no-default-export
export default EmbedFrame;
Loading