From 77058f9955ec39907cc755154930181e4e489c83 Mon Sep 17 00:00:00 2001 From: Bianca Del Carretto Date: Mon, 8 Jun 2020 22:26:40 +0200 Subject: [PATCH 1/6] enable html mixed mode (#10) --- src/components/Editor.js | 7 +++++++ src/components/MarkupEditor.js | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/components/Editor.js b/src/components/Editor.js index 2c8e8a0a..5cef7f9f 100644 --- a/src/components/Editor.js +++ b/src/components/Editor.js @@ -1,6 +1,8 @@ import React, { useRef, useEffect } from 'react'; import 'codemirror/mode/javascript/javascript'; import 'codemirror/mode/xml/xml'; +import 'codemirror/mode/css/css'; +import 'codemirror/mode/htmlmixed/htmlmixed'; import 'codemirror/addon/edit/closetag'; import 'codemirror/addon/fold/xml-fold'; import 'codemirror/addon/scroll/simplescrollbars'; @@ -27,6 +29,11 @@ const options = { mode: { name: 'text/html', multilineTagIndentPastTag: false }, }, + htmlmixed: { + ...baseOptions, + mode: { name: 'htmlmixed' }, + }, + javascript: { ...baseOptions, mode: 'javascript', diff --git a/src/components/MarkupEditor.js b/src/components/MarkupEditor.js index 7c86a6a9..d9da0b0a 100644 --- a/src/components/MarkupEditor.js +++ b/src/components/MarkupEditor.js @@ -16,7 +16,7 @@ function MarkupEditor({ markup, dispatch }) {
Date: Wed, 10 Jun 2020 16:54:52 +0200 Subject: [PATCH 2/6] enable interaction in Preview panel through script tag in html (#10) --- src/components/Preview.js | 36 ++++++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/components/Preview.js b/src/components/Preview.js index 0bb2aaf8..4805908f 100644 --- a/src/components/Preview.js +++ b/src/components/Preview.js @@ -1,4 +1,4 @@ -import React, { useState, useEffect, useRef } from 'react'; +import React, { useState, useEffect, useRef, useMemo } from 'react'; import Scrollable from './Scrollable'; import PreviewHint from './PreviewHint'; import AddHtml from './AddHtml'; @@ -34,6 +34,36 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { // TestingLibraryDom?.getSuggestedQuery(highlighted, 'get').toString() : null + const scripts = useMemo(() => { + const container = document.createElement('div'); + container.innerHTML = markup; + const scriptsCollections = container.getElementsByTagName('script'); + return Array.from(scriptsCollections).map((script) => script.innerHTML); + }, [markup]); + + const actualMarkup = useMemo( + () => + scripts.length + ? scripts.reduce( + (html, script) => html.replace(``, ''), + markup, + ) + : markup, + [scripts, markup], + ); + + useEffect(() => { + if (scripts.length) { + try { + scripts.forEach((script) => { + window.eval(script); + }); + } catch { + return; + } + } + }, [scripts]); + useEffect(() => { setRoles(Object.keys(accessibleRoles || {}).sort()); }, [accessibleRoles]); @@ -98,7 +128,9 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { onClick={handleClick} onMouseMove={handleMove} ref={htmlRoot} - dangerouslySetInnerHTML={{ __html: markup }} + dangerouslySetInnerHTML={{ + __html: actualMarkup, + }} />
From 3db3273b91815f2dd52d02ee388c18e4e3eab1c3 Mon Sep 17 00:00:00 2001 From: Bianca Del Carretto Date: Thu, 11 Jun 2020 22:43:47 +0200 Subject: [PATCH 3/6] handle (and eval only) scripts with type text/javascript and with undefined type --- src/components/Preview.js | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/src/components/Preview.js b/src/components/Preview.js index 4805908f..d25c92bd 100644 --- a/src/components/Preview.js +++ b/src/components/Preview.js @@ -38,14 +38,21 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { const container = document.createElement('div'); container.innerHTML = markup; const scriptsCollections = container.getElementsByTagName('script'); - return Array.from(scriptsCollections).map((script) => script.innerHTML); + return Array.from(scriptsCollections) + .filter( + (script) => script.type === 'text/javascript' || script.type === '', + ) + .map((script) => ({ + scriptCode: script.innerHTML, + toBeRemoved: script.outerHTML, + })); }, [markup]); const actualMarkup = useMemo( () => scripts.length ? scripts.reduce( - (html, script) => html.replace(``, ''), + (html, script) => html.replace(script.toBeRemoved, ''), markup, ) : markup, @@ -56,7 +63,7 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { if (scripts.length) { try { scripts.forEach((script) => { - window.eval(script); + window.eval(script.scriptCode); }); } catch { return; From cdf21f6a7385c1feeb7c5675889dbe50b85e08a7 Mon Sep 17 00:00:00 2001 From: Bianca Del Carretto Date: Thu, 11 Jun 2020 23:38:03 +0200 Subject: [PATCH 4/6] fix script evaluation on mount (without editing html markup) and replace eval with new Function (#10) --- src/components/Preview.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/Preview.js b/src/components/Preview.js index d25c92bd..7c0e84e8 100644 --- a/src/components/Preview.js +++ b/src/components/Preview.js @@ -60,16 +60,17 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { ); useEffect(() => { - if (scripts.length) { + if (scripts.length && htmlRoot.current) { try { scripts.forEach((script) => { - window.eval(script.scriptCode); + const executeScript = new Function(script.scriptCode); + executeScript(); }); } catch { return; } } - }, [scripts]); + }, [scripts, htmlRoot.current]); useEffect(() => { setRoles(Object.keys(accessibleRoles || {}).sort()); From 97909a9f2e73cd4191da6b06d41785b39186f89f Mon Sep 17 00:00:00 2001 From: Bianca Del Carretto Date: Fri, 12 Jun 2020 08:52:56 +0200 Subject: [PATCH 5/6] move try catch in forEach loop and add alert for a failing script (#10) --- src/components/Preview.js | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/components/Preview.js b/src/components/Preview.js index 7c0e84e8..0dcdde43 100644 --- a/src/components/Preview.js +++ b/src/components/Preview.js @@ -60,15 +60,15 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { ); useEffect(() => { - if (scripts.length && htmlRoot.current) { - try { - scripts.forEach((script) => { + if (htmlRoot.current) { + scripts.forEach((script) => { + try { const executeScript = new Function(script.scriptCode); executeScript(); - }); - } catch { - return; - } + } catch (e) { + alert('Failing script inserted in markup!'); + } + }); } }, [scripts, htmlRoot.current]); From 2e864429669eb157a0119319f5a90eb2fa5937aa Mon Sep 17 00:00:00 2001 From: Bianca Del Carretto Date: Fri, 12 Jun 2020 09:38:40 +0200 Subject: [PATCH 6/6] add a map to evaluate each script just once (#10) --- src/components/Preview.js | 58 +++++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 20 deletions(-) diff --git a/src/components/Preview.js b/src/components/Preview.js index 0dcdde43..1d4ca45e 100644 --- a/src/components/Preview.js +++ b/src/components/Preview.js @@ -25,6 +25,7 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { // Indicating that the `parsed` element can be highlighted again. const [highlighted, setHighlighted] = useState(false); const [roles, setRoles] = useState([]); + const [scripts, setScripts] = useState([]); const htmlRoot = useRef(); const { suggestion } = getQueryAdvise({ @@ -34,19 +35,33 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { // TestingLibraryDom?.getSuggestedQuery(highlighted, 'get').toString() : null - const scripts = useMemo(() => { + useEffect(() => { const container = document.createElement('div'); container.innerHTML = markup; const scriptsCollections = container.getElementsByTagName('script'); - return Array.from(scriptsCollections) - .filter( - (script) => script.type === 'text/javascript' || script.type === '', - ) - .map((script) => ({ - scriptCode: script.innerHTML, - toBeRemoved: script.outerHTML, - })); - }, [markup]); + const jsScripts = Array.from(scriptsCollections).filter( + (script) => script.type === 'text/javascript' || script.type === '', + ); + setScripts((scripts) => [ + ...scripts.filter((script) => + jsScripts + .map((jsScript) => jsScript.innerHTML) + .includes(script.innerHTML), + ), + ...jsScripts + .filter( + (jsScript) => + !scripts + .map((script) => script.innerHTML) + .includes(jsScript.innerHTML), + ) + .map((jsScript) => ({ + scriptCode: jsScript.innerHTML, + toBeRemoved: jsScript.outerHTML, + evaluated: false, + })), + ]); + }, [markup, setScripts]); const actualMarkup = useMemo( () => @@ -60,17 +75,20 @@ function Preview({ markup, accessibleRoles, elements, dispatch }) { ); useEffect(() => { - if (htmlRoot.current) { - scripts.forEach((script) => { - try { - const executeScript = new Function(script.scriptCode); - executeScript(); - } catch (e) { - alert('Failing script inserted in markup!'); - } - }); + if (htmlRoot.current && highlighted) { + scripts + .filter((script) => !script.evaluated) + .forEach((script) => { + try { + script.evaluated = true; + const executeScript = new Function(script.scriptCode); + executeScript(); + } catch (e) { + alert('Failing script inserted in markup!'); + } + }); } - }, [scripts, htmlRoot.current]); + }, [highlighted, scripts, htmlRoot.current]); useEffect(() => { setRoles(Object.keys(accessibleRoles || {}).sort());