Skip to content

Commit b5fc294

Browse files
committed
Remove horrible iframe overlay hack, introduce sandbox targets
1 parent 1e940a5 commit b5fc294

File tree

4 files changed

+93
-71
lines changed

4 files changed

+93
-71
lines changed

asciidoc_html.conf

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ options=sectionbody
3636
</div></div>
3737

3838
[source-highlight-block]
39-
<pre{id? id="{id}"} data-language="{language}" class="cm-s-default">
39+
<pre{id? id="{id}"} data-language="{language}" class="snippet cm-s-default"{sandbox? data-sandbox="{sandbox}"}>
4040
|
4141
</pre>
4242

html/ejs.css

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,6 @@ blockquote footer:before {
183183
-webkit-transition: margin-left .5s ease-out, margin-right .5s ease-out;
184184
-o-transition: margin-left .5s ease-out, margin-right .5s ease-out;
185185
transition: margin-left .5s ease-out, margin-right .5s ease-out;
186-
border-top: 1px solid #4ab;
187186
border-bottom: 1px solid #4ab;
188187
}
189188

@@ -199,10 +198,11 @@ blockquote footer:before {
199198
display: none;
200199
}
201200

202-
#sandbox {
201+
.editor-wrap iframe {
202+
display: block;
203+
border: 1px dotted #4ab;
203204
border-top: 1px solid #4ab;
204-
border-left: 1px dotted #4ab;
205-
border-right: 1px dotted #4ab;
205+
border-bottom-width: 0;
206206
padding: 0; margin: 0;
207207
width: 100%;
208208
-moz-box-sizing: border-box;
@@ -299,6 +299,7 @@ blockquote footer:before {
299299
.CodeMirror {
300300
height: auto;
301301
line-height: 1.3;
302+
border-top: 1px solid #4ab;
302303
}
303304
.CodeMirror-scroll {
304305
max-height: 700px;

html/js/ejs.js

Lines changed: 65 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,6 @@ window.addEventListener("load", function() {
22
// If there's no ecmascript 5 support, don't try to initialize
33
if (!Object.create || !window.JSON) return;
44

5-
var sandbox;
6-
function resetSandbox() {
7-
sandbox = new SandBox({loadFiles: window.sandboxLoadFiles});
8-
window.sandbox = sandbox;
9-
}
10-
resetSandbox();
11-
125
document.body.addEventListener("click", function(e) {
136
for (var n = e.target; n; n = n.parentNode) {
147
if (n.className == "c_ident") return;
@@ -45,26 +38,38 @@ window.addEventListener("load", function() {
4538
"Ctrl-Q": resetSandbox
4639
};
4740

41+
var nextID = 0;
42+
4843
function activateCode(node, e, lang) {
4944
var code = node.textContent;
5045
node.style.display = "none";
5146
var wrap = node.parentNode.insertBefore(elt("div", {"class": "editor-wrap"}), node);
52-
var editor = CodeMirror(wrap, {
47+
var editor = CodeMirror(function(div) {wrap.insertBefore(div, wrap.firstChild)}, {
5348
value: code,
5449
mode: lang,
5550
extraKeys: keyMap,
5651
matchBrackets: true,
5752
lineNumbers: true
5853
});
59-
editor.on("change", SandBox.Output.repositionFrame);
60-
wrap.style.margin = "0 -5em";
61-
setTimeout(function() { editor.refresh(); SandBox.Output.repositionFrame(); }, 600);
62-
editor.setCursor(editor.coordsChar({left: e.clientX, top: e.clientY}, "client"));
63-
editor.focus();
54+
wrap.style.margin = "1rem -5em";
55+
setTimeout(function() { editor.refresh(); }, 600);
56+
if (e) {
57+
editor.setCursor(editor.coordsChar({left: e.clientX, top: e.clientY}, "client"));
58+
editor.focus();
59+
}
6460
var out = wrap.appendChild(elt("div", {"class": "sandbox-output"}));
6561
var menu = wrap.appendChild(elt("div", {"class": "sandbox-menu", title: "Sandbox menu..."}));
62+
var sandbox = node.getAttribute("data-sandbox");
63+
if (lang == "text/html" && !sandbox) {
64+
sandbox = "html" + nextID++;
65+
node.setAttribute("data-sandbox", sandbox);
66+
}
6667

67-
var data = editor.state.context = {editor: editor, wrap: wrap, orig: node, isHTML: lang == "text/html"};
68+
var data = editor.state.context = {editor: editor,
69+
wrap: wrap,
70+
orig: node,
71+
isHTML: lang == "text/html",
72+
sandbox: sandbox};
6873
data.output = new SandBox.Output(out);
6974
menu.addEventListener("click", function() { openMenu(data, menu); });
7075
}
@@ -73,8 +78,9 @@ window.addEventListener("load", function() {
7378
var menu = elt("div", {"class": "sandbox-open-menu"});
7479
var items = [["Run code (ctrl-enter)", function() { runCode(data); }],
7580
["Revert to original code", function() { revertCode(data); }],
76-
["Reset sandbox (ctrl-q)", resetSandbox],
77-
["Deactivate editor (ctrl-d)", function() { closeCode(data); }]];
81+
["Reset sandbox (ctrl-q)", resetSandbox]];
82+
if (!data.isHTML || !data.sandbox)
83+
items.push(["Deactivate editor (ctrl-d)", function() { closeCode(data); }]);
7884
items.forEach(function(choice) {
7985
menu.appendChild(elt("div", choice[0]));
8086
});
@@ -96,20 +102,59 @@ window.addEventListener("load", function() {
96102

97103
function runCode(data) {
98104
data.output.clear();
99-
var val = data.editor.getValue();
105+
var val = data.editor.getValue(), box = getSandbox(data.sandbox, data.bisHTML);
100106
if (data.isHTML)
101-
sandbox.show(val, data.output);
107+
box.setHTML(val, data.output);
102108
else
103-
sandbox.run(val, data.output);
109+
box.run(val, data.output);
104110
}
105111

106112
function closeCode(data) {
113+
if (data.isHTML && data.sandbox) return;
107114
data.wrap.parentNode.removeChild(data.wrap);
108115
data.orig.style.display = "";
109-
SandBox.Output.repositionFrame();
110116
}
111117

112118
function revertCode(data) {
113119
data.editor.setValue(data.orig.textContent);
114120
}
121+
122+
var sandboxes = {};
123+
function getSandbox(name, forHTML) {
124+
name = name || "null";
125+
if (sandboxes.hasOwnProperty(name)) return sandboxes[name];
126+
var options = {loadFiles: window.sandboxLoadFiles}, html;
127+
if (name != "null") {
128+
var snippets = document.getElementsByClassName("snippet");
129+
for (var i = 0; i < snippets.length; i++) {
130+
var snippet = snippets[i];
131+
if (snippet.getAttribute("data-language") == "text/html" &&
132+
snippet.getAttribute("data-sandbox") == name) {
133+
options.place = function(node) { placeFrame(node, snippet); };
134+
if (!forHTML) html = snippet.textContent;
135+
break;
136+
}
137+
}
138+
}
139+
var box = sandboxes[name] = new SandBox(options);
140+
if (html != null) box.win.document.documentElement.innerHTML = html;
141+
return box;
142+
}
143+
144+
function resetSandbox(name) {
145+
name = name || "null";
146+
if (!sandboxes.hasOwnProperty(name)) return;
147+
var frame = sandboxes[name].frame;
148+
frame.parentNode.removeChild(frame);
149+
delete sandboxes[name];
150+
}
151+
152+
function placeFrame(frame, snippet) {
153+
var wrap = snippet.previousSibling;
154+
if (!wrap || wrap.className != "editor-wrap") {
155+
activateCode(snippet, null, "text/html");
156+
wrap = snippet.previousSibling;
157+
}
158+
wrap.insertBefore(frame, wrap.childNodes[1]);
159+
}
115160
});

html/js/sandbox.js

Lines changed: 22 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@
99
found.push({fn: m[1] || m[3] || null,
1010
line: m[2] || m[4]});
1111
}
12-
console.log(stack);
1312
return found;
1413
}
1514
function frameString(frame) {
@@ -18,10 +17,13 @@
1817

1918
var SandBox = window.SandBox = function(options) {
2019
var frame = this.frame = document.createElement("iframe");
21-
frame.id = "sandbox";
22-
frame.style.display = "none";
2320
frame.src = "about:blank";
24-
document.body.appendChild(frame);
21+
if (options.place) {
22+
options.place(frame);
23+
} else {
24+
frame.style.display = "none";
25+
document.body.appendChild(frame);
26+
}
2527

2628
var win = this.win = frame.contentWindow;
2729
var self = win.__sandbox = this;
@@ -72,10 +74,10 @@
7274
this.win.__c = 0;
7375
timeout(this.win, preprocess(code, this), 0);
7476
},
75-
show: function(html, output) {
77+
setHTML: function(code, output) {
7678
var scriptTags = [], sandbox = this, doc = this.win.document;
7779
this.frame.style.display = "block";
78-
doc.documentElement.innerHTML = html.replace(/<script\b[^>]*?(?:\bsrc\s*=\s*('[^']+'|"[^"]+"|[^\s>]+)[^>]*)?>([\s\S]*?)<\/script>/, function(m, src, content) {
80+
doc.documentElement.innerHTML = code.replace(/<script\b[^>]*?(?:\bsrc\s*=\s*('[^']+'|"[^"]+"|[^\s>]+)[^>]*)?>([\s\S]*?)<\/script>/, function(m, src, content) {
7981
var tag = doc.createElement("script");
8082
if (src) {
8183
if (/["']/.test(src.charAt(0))) src = src.slice(1, src.length - 1);
@@ -87,14 +89,21 @@
8789
return "";
8890
});
8991

90-
if (output) {
91-
this.output = output;
92-
output.addFrame(this.frame);
92+
this.frame.style.height = "10px";
93+
this.resizeFrame();
94+
if (scriptTags.length) {
95+
if (output) this.output = output;
96+
this.startedAt = Date.now();
97+
this.extraSecs = 1;
98+
this.win.__c = 0;
99+
var resize = doc.createElement("script");
100+
resize.innerHTML = "__sandbox.resizeFrame();";
101+
scriptTags.push(resize);
102+
scriptTags.forEach(function(tag) { doc.body.appendChild(tag); });
93103
}
94-
this.startedAt = Date.now();
95-
this.extraSecs = 1;
96-
this.win.__c = 0;
97-
scriptTags.forEach(function(tag) { doc.body.appendChild(tag); });
104+
},
105+
resizeFrame: function() {
106+
this.frame.style.height = Math.max(80, Math.min(this.frame.contentWindow.document.body.scrollHeight, 600)) + "px";
98107
},
99108
tick: function() {
100109
var now = Date.now();
@@ -165,33 +174,10 @@
165174
return "try{" + out + "\n}catch(e){__sandbox.error(e);}";
166175
}
167176

168-
function pageScrollX() { return window.pageXOffset || (document.documentElement || document.body).scrollLeft; }
169-
function pageScrollY() { return window.pageYOffset || (document.documentElement || document.body).scrollTop; }
170-
171177
var Output = SandBox.Output = function(div) {
172178
this.div = div;
173179
};
174180

175-
var currentPlaceHolder = null;
176-
function positionFrame(node) {
177-
node.style.width = currentPlaceHolder.offsetWidth + "px";
178-
node.style.height = currentPlaceHolder.offsetHeight + "px";
179-
var box = currentPlaceHolder.getBoundingClientRect();
180-
node.style.left = (box.left + pageScrollX()) + "px";
181-
node.style.top = (box.top + pageScrollY()) + "px";
182-
}
183-
184-
window.addEventListener("resize", Output.repositionFrame = function() {
185-
if (currentPlaceHolder) {
186-
if (currentPlaceHolder.parentNode) {
187-
positionFrame(currentPlaceHolder.frame);
188-
} else {
189-
currentPlaceHolder.frame.style.display = "none";
190-
currentPlaceHolder = false;
191-
}
192-
}
193-
});
194-
195181
Output.prototype = {
196182
clear: function() { this.div.innerHTML = ""; },
197183
out: function(type, args) {
@@ -206,16 +192,6 @@
206192
wrap.appendChild(represent(arg, 58));
207193
}
208194
this.div.appendChild(wrap);
209-
},
210-
addFrame: function(node) {
211-
if (currentPlaceHolder && currentPlaceHolder.parentNode)
212-
currentPlaceHolder.parentNode.removeChild(currentPlaceHolder);
213-
currentPlaceHolder = this.div.parentNode.insertBefore(document.createElement("div"), this.div);
214-
currentPlaceHolder.innerHTML = "&nbsp;";
215-
currentPlaceHolder.style.height = Math.max(100, Math.min(node.contentWindow.document.body.clientHeight, 600)) + "px";
216-
currentPlaceHolder.frame = node;
217-
node.style.position = "absolute";
218-
positionFrame(node);
219195
}
220196
};
221197

0 commit comments

Comments
 (0)