forked from stevekrouse/WoofJS
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathrun-code.js
More file actions
130 lines (120 loc) · 4.39 KB
/
run-code.js
File metadata and controls
130 lines (120 loc) · 4.39 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
// this builds a babel plugin that will inject break statements into loops
// to prevent them from running for too long. Inspired by:
// https://github.com/codepen/InfiniteLoopBuster
function buildBreakCheck(loopID, lineNum, t) {
return t.ifStatement(
t.callExpression(
t.identifier('_shouldThrowError'),
[
t.numericLiteral(loopID),
t.numericLiteral(lineNum)
]
),
t.blockStatement([
t.breakStatement()
])
);
}
let loopIdCounter = 1;
function inject(path, t) {
// this if statement seems necessary when parsing classes(?)
if (!path.node.loc) {
return
}
const { line } = path.node.loc.start;
const body = path.node.body;
if (!t.isBlockStatement(body)) {
path.node.body = t.blockStatement([buildBreakCheck(loopIdCounter, line, t), body]);
} else {
body.body.unshift(buildBreakCheck(loopIdCounter, line, t));
}
loopIdCounter++;
}
const injectLoopBreakerPlugin = ({types: t}) => ({
visitor: {
ForStatement(path) {
inject(path, t);
},
WhileStatement(path) {
inject(path, t);
},
ForInStatement(path) {
inject(path, t);
},
ForOfStatement(path) {
inject(path, t);
},
},
});
// this is a helper function that should only be called by runCode
function tryRunningCode(doc, codeValue, errorCallback) {
try {
// loop-bust the code (this used to do backwards compatibility as well)
var result = Babel.transform(codeValue, {
plugins: [injectLoopBreakerPlugin],
retainLines: true,
})
var code = result.code;
var script = doc.createElement("script");
script.type = "text/javascript";
script.crossorigin = "anonymous";
script.text = code;
doc.body.appendChild(script);
}
// this catch is mainly for the Babel.transform:
// if the user's code is incorrectly formatted, Babel will
// throw an error while trying to parse it
// This approximates a compile-time error, while run time errors
// will need to be handled separately on the document's window
// (not done in this file since different uses have different ways of handling them)
catch (e) {
errorCallback(e)
}
}
// this is the main function that should be used
//
// this runs the user's code in the provided document
//
// Prerequisites:
// This uses babel for parsing and busting infinite loops
// HTML pages that import this should also import:
// <script src="https://unpkg.com/@babel/standalone@7.21.4/babel.min.js"></script>
//
// Parameters:
// doc - an HTML document that code should be run in.
// It can be either the base document,
// or the document component of an iframe (iframe.contentWindow.document)
// codeValue - a string containing all the user code to run.
// errorCallback - a function that will handle errors
//
// Properties:
// The code provided in codeValue will be attached to doc to be run.
// There may be some modifications that do not effect the code for compatibility purposes
// Any For/While loops will have an extra conditional inserted at the beginning of the
// body of the loop to check for a long-running loop, which will break with an error
// in order to avoid the page hanging, which can result in the inability to save work
// If codeValue is improperly formatted, codeValue will not be attached to doc,
// and the errorCallback will be called with the Error event.
// This is not the only way the user's code can create errors! "Run-time" errors are not
// handled in this process, and the document/window is responsible for catching and
// processing those errors.
function runCode(doc, codeValue, errorCallback) {
setTimeout(function() {
// add a base tag to the page so it knows where to pull relative image urls
var base = doc.createElement("base");
base.href = document.baseURI
doc.body.appendChild(base);
// then we create a script tag with the woof code and add it to the page
var script = doc.createElement("script");
script.type = "text/javascript";
script.src = "./woof.js";
doc.body.appendChild(script);
script.onload = function() {
// when the woof.js library loads, trigger load events (for Woof setup)
const evt = new Event('load', { bubbles: false, cancelable: false });
doc.defaultView.dispatchEvent(evt);
// then run the user code
tryRunningCode(doc, codeValue, errorCallback);
}
}, 10)
}