Skip to content

Commit ef8a2a3

Browse files
author
Ke, Mingze
committed
Eval code in the iframe sandbox
1 parent cc1b460 commit ef8a2a3

5 files changed

Lines changed: 201 additions & 208 deletions

File tree

code.js

Lines changed: 177 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -84,12 +84,16 @@ Code.getPage = function () {
8484
Code.getDemoPage = function () {
8585
var area = document.querySelector('#demo-area.show');
8686
if (area) {
87-
var demo = area.querySelector('.da.show');
88-
if (demo) {
89-
return demo.id;
87+
var select = document.querySelector('#demo-select');
88+
var link;
89+
if ((select.value) * 1 < 10) {
90+
link = '0' + select.value;
91+
} else {
92+
link = select.value;
9093
}
94+
return 'demo-area-' + link;
9195
}
92-
return '';
96+
return 'default';
9397
};
9498

9599
/**
@@ -314,98 +318,81 @@ Code.copyCode = function (copy) {
314318
}
315319

316320
Code.loadDemoArea = function () {
317-
var btn, area, select, close, da, option, contentHeight, resizeBar, array=[];
318-
area = document.getElementById('demo-area');
319-
function content(p) {
320-
var i;
321-
array.forEach(function(e,i){
322-
da[i].className = da[i].className.replace("show", "");
323-
});
324-
option[p - 1].selected = true;
325-
document.getElementById('demo-area-0' + p).className = document.getElementById('demo-area-0' + p).className + " show";
326-
localStorage.demoAreaSelect = p;
321+
var area = document.getElementById('demo-area');
322+
var btn = document.getElementById('demoButton');
323+
var select = document.getElementById('demo-select');
324+
var close = document.querySelector('.close-btn');
325+
var contentHeight = document.getElementById('content_blocks').offsetHeight;
326+
var resizeBar = document.getElementById('demo-resize-bar');
327+
328+
area.style.height = (contentHeight - 130) + 'px';
329+
area.className = area.className.replace("show", "");
330+
331+
if (localStorage.demoAreaWidth) {
332+
area.style.width = localStorage.demoAreaWidth;
327333
}
328-
if (area) {
329-
btn = document.getElementById('demoButton');
330-
select = document.getElementById('demo-select');
331-
close = document.querySelector('.close-btn');
332-
da = document.querySelectorAll('.da');
333-
option = document.querySelectorAll('#demo-select option');
334-
contentHeight = document.getElementById('content_blocks').offsetHeight;
335-
resizeBar = document.getElementById('demo-resize-bar');
336-
337-
array= Array.from(da);
338-
area.style.height = (contentHeight - 130) + 'px';
339-
area.className = area.className.replace("show", "");
340334

341-
if (localStorage.demoAreaWidth) {
342-
area.style.width = localStorage.demoAreaWidth;
343-
}
335+
if (localStorage.demoArea == 'open') {
336+
area.className = area.className + "show";
337+
}
344338

345-
if (localStorage.demoArea == 'open') {
346-
area.className = area.className + "show";
347-
//btn.style.color = '#f80';
348-
btn.style.opacity = 1;
349-
}
339+
if (!localStorage.demoAreaSelect) {
340+
localStorage.demoAreaSelect = 1;
341+
}
350342

351-
content(localStorage.demoAreaSelect || 1);
343+
select.value = localStorage.demoAreaSelect;
344+
Code.reloadSandbox();
352345

353-
window.addEventListener('resize', function () {
354-
contentHeight = document.getElementById('content_blocks').offsetHeight;
355-
area.style.height = (contentHeight - 130) + 'px';
356-
});
346+
window.addEventListener('resize', function () {
347+
contentHeight = document.getElementById('content_blocks').offsetHeight;
348+
area.style.height = (contentHeight - 130) + 'px';
349+
});
357350

358-
resizeBar.onmousedown = function (e) {
359-
area.style.opacity = '0.4';
360-
var ox = e.pageX;
361-
var dw = area.offsetWidth;
362-
area.className = area.className + " resize";
363-
document.onmousemove = function (event) {
364-
var rx = event.pageX;
365-
area.style.width = dw - rx + ox - 20 + 'px';
366-
localStorage.demoAreaWidth = area.style.width;
367-
}
368-
}
351+
resizeBar.onmousedown = function (e) {
352+
area.style.opacity = '0.4';
353+
var frame = document.getElementById('demo-frame');
354+
frame.style.pointerEvents = 'none';
355+
var ox = e.pageX;
356+
var dw = area.offsetWidth;
357+
area.className += " resize";
358+
359+
document.onmousemove = function (event) {
360+
var rx = event.pageX;
361+
area.style.width = dw - rx + ox - 20 + 'px';
362+
localStorage.demoAreaWidth = area.style.width;
363+
};
364+
};
369365

370-
document.onmouseup = function () {
371-
area.style.opacity = '1';
372-
area.className = area.className.replace(" resize", "");
373-
document.onmousemove = null;
374-
}
366+
document.onmouseup = function () {
367+
area.style.opacity = '1';
368+
var frame = document.getElementById('demo-frame');
369+
frame.style.pointerEvents = 'auto';
370+
area.className = area.className.replace("resize", "");
371+
document.onmousemove = null;
372+
};
375373

376-
btn.onclick = function () {
377-
area.className = area.className.replace("show", "");
378-
if (localStorage.demoArea == 'open') {
379-
localStorage.demoArea = 'close';
380-
btn.style.color = '#000';
381-
btn.style.opacity = 0.8;
382-
} else {
383-
area.className = area.className + "show";
384-
localStorage.demoArea = 'open';
385-
//btn.style.color = '#f80';
386-
btn.style.opacity = 1;
387-
}
388-
};
389-
close.onclick = function () {
374+
btn.onclick = function () {
375+
if (localStorage.demoArea == 'open') {
390376
area.className = area.className.replace("show", "");
391377
localStorage.demoArea = 'close';
392-
btn.style.color = '#000';
393-
btn.style.opacity = 0.8;
394-
};
378+
} else {
379+
area.className += " show";
380+
localStorage.demoArea = 'open';
381+
}
382+
Code.reloadSandbox();
383+
};
395384

396-
select.addEventListener('change', function () {
397-
var i;
398-
var selectValue = this.value;
399-
var selectId = 'demo-area-0' + selectValue;
400-
ga('send', 'event', 'Webduino-blockly', 'demo select', selectValue);
401-
localStorage.demoAreaSelect = selectValue;
402-
array.forEach(function(e,i){
403-
da[i].className = da[i].className.replace("show", "");
404-
});
405-
document.getElementById(selectId).className = document.getElementById(selectId).className + " show"
406-
});
407-
}
408-
}
385+
close.onclick = function () {
386+
area.className = area.className.replace("show", "");
387+
localStorage.demoArea = 'close';
388+
};
389+
390+
select.onchange = function () {
391+
ga('send', 'event', 'Webduino-blockly', 'demo select', this.value);
392+
localStorage.demoAreaSelect = this.value;
393+
Code.reloadSandbox();
394+
};
395+
};
409396

410397
Code.loadSample = function () {
411398
var sampleBtn = document.getElementById('sampleButton'),
@@ -487,6 +474,10 @@ Code.LANG = Code.getLang();
487474

488475
Code.PAGE = Code.getPage();
489476

477+
Code.running = false;
478+
479+
Code.lastRun = 0;
480+
490481
/**
491482
* List of tab names.
492483
* @private
@@ -649,22 +640,10 @@ Code.init = function() {
649640
Code.tabClick(Code.selected);
650641

651642
Code.bindClick('linkToBin', function () {
652-
var urls = (location.protocol + '//' + location.host + location.pathname).split('/'),
653-
code = Blockly.JavaScript.workspaceToCode(Code.workspace),
654-
babelize = code.indexOf('async function') !== -1,
655-
page = Code.getPage(),
656-
config = {
657-
tpl: page === 'index' ? Code.getDemoPage() : page,
658-
modes: 'html,css,js,output',
659-
data: {
660-
js: babelize ?
661-
'(async function () {\n\n' + code + '\n}());' : code
662-
},
663-
jsPreprocessor: babelize ? 'babel' : ''
664-
};
665-
643+
var urls = (location.protocol + '//' + location.host + location.pathname).split('/');
644+
var ctx = Code.getContext();
666645
urls.pop();
667-
localStorage.setItem(urls.join('/') + '/launcher.html', JSON.stringify(config));
646+
localStorage.setItem(urls.join('/') + '/launcher.html', JSON.stringify(ctx));
668647
});
669648

670649
Code.bindClick('trashButton',
@@ -698,6 +677,20 @@ Code.init = function() {
698677
window.setTimeout(Code.copyCode, 1);
699678
window.setTimeout(Code.ga, 1);
700679

680+
var eventDelegater = function (e) {
681+
var frame = document.getElementById('demo-frame'),
682+
doc = frame.contentWindow.document;
683+
684+
if (Code.running) {
685+
var event = doc.createEvent('Event');
686+
event.initEvent(e.type, true, true);
687+
event.key = e.key;
688+
event.keyCode = e.keyCode;
689+
doc.body.dispatchEvent(event);
690+
}
691+
};
692+
window.addEventListener('keydown', eventDelegater);
693+
window.addEventListener('keyup', eventDelegater);
701694
};
702695

703696
Code.renderPage = function (callback) {
@@ -779,27 +772,90 @@ Code.initLanguage = function() {
779772
* Execute the user's code.
780773
* Just a quick and dirty eval. Catch infinite loops.
781774
*/
782-
Code.runJS = function() {
783-
Blockly.JavaScript.INFINITE_LOOP_TRAP = ' checkTimeout();\n';
784-
var timeouts = 0;
785-
var checkTimeout = function() {
786-
if (timeouts++ > 1000000) {
787-
throw MSG['timeout'];
788-
}
789-
};
790-
var code = Blockly.JavaScript.workspaceToCode(Code.workspace);
791-
if (code.indexOf('async function') === -1) {
792-
code = 'disconnectBoards(function () {' + code + '})';
793-
} else {
794-
code = Code.transform('disconnectBoards(async function () {' + code + '})');
775+
Code.runJS = function () {
776+
var now = Date.now();
777+
778+
if (now - Code.lastRun < 500) {
779+
return;
795780
}
781+
Code.lastRun = now;
796782

797-
Blockly.JavaScript.INFINITE_LOOP_TRAP = null;
798-
try {
799-
eval(code);
800-
} catch (e) {
801-
alert(MSG['badCode'].replace('%1', e));
783+
Code.toggleRunning();
784+
Code.reloadSandbox();
785+
};
786+
787+
Code.toggleRunning = function () {
788+
var runBtn = document.getElementById('runButton');
789+
var select = document.getElementById('demo-select');
790+
var demoBtn = document.getElementById('demoButton');
791+
var demoStopBtn = document.querySelector('.close-btn');
792+
793+
if (!Code.running) {
794+
runBtn.style.backgroundColor = "#0a5";
795+
runBtn.style.borderColor = "#0a5";
796+
document.querySelector('#runButton i').setAttribute('class', 'icon-stop2');
797+
document.querySelector('#runButton div').innerHTML = "停止執行";
798+
select.disabled = true;
799+
demoBtn.disabled = true;
800+
demoBtn.style.opacity = 0.5;
801+
demoStopBtn.style.pointerEvents = 'none';
802+
demoStopBtn.style.opacity = 0.2;
803+
} else {
804+
runBtn.style.backgroundColor = "#dd4b39";
805+
runBtn.style.borderColor = "#dd4b39";
806+
document.querySelector('#runButton i').setAttribute('class', 'icon-play3');
807+
document.querySelector('#runButton div').innerHTML = "執行程式";
808+
select.disabled = false;
809+
demoBtn.disabled = false;
810+
demoBtn.style.opacity = 0.9;
811+
demoStopBtn.style.pointerEvents = 'auto';
812+
demoStopBtn.style.opacity = 0.5;
802813
}
814+
815+
Code.running = !Code.running;
816+
};
817+
818+
Code.reloadSandbox = function () {
819+
var container = document.querySelector('.demo-area-content');
820+
var ctx = Code.getContext();
821+
822+
launcher.loadTemplate('./templates/' + ctx.tpl + '.html', function (data) {
823+
if (Code.running) {
824+
if (ctx.jsPreprocessor === 'babel') {
825+
data.js = Code.transform(ctx.data.js);
826+
} else {
827+
data.js = ctx.data.js;
828+
}
829+
}
830+
831+
var frame = container.querySelector('#demo-frame');
832+
if (frame) {
833+
container.removeChild(frame);
834+
frame = null;
835+
}
836+
837+
frame = document.createElement('iframe');
838+
frame.id = 'demo-frame';
839+
frame.style.display = 'block';
840+
container.appendChild(frame);
841+
842+
launcher.sandbox(frame, data);
843+
});
844+
};
845+
846+
Code.getContext = function () {
847+
var code = Blockly.JavaScript.workspaceToCode(Code.workspace),
848+
babelize = code.indexOf('async function') !== -1,
849+
page = Code.getPage();
850+
851+
return {
852+
tpl: page === 'index' ? Code.getDemoPage() : page,
853+
modes: 'html,css,js,output',
854+
data: {
855+
js: code
856+
},
857+
jsPreprocessor: babelize ? 'babel' : ''
858+
};
803859
};
804860

805861
Code.transform = function (code) {
@@ -811,7 +867,7 @@ Code.transform = function (code) {
811867
} catch (e) {
812868
alert(e);
813869
}
814-
}
870+
};
815871

816872
/**
817873
* Discard all blocks from the workspace.
@@ -908,6 +964,8 @@ Blockly.JavaScript['workspaceToCode'] = function (workspace) {
908964
var code = Blockly.JavaScript['_workspaceToCode'].call(Blockly.JavaScript, workspace);
909965
if (code.indexOf('await ') === -1) {
910966
code = code.replace(new RegExp('async function', 'g'), 'function');
967+
} else {
968+
code = '(async function () {\n\n' + code + '\n}());';
911969
}
912970
return code;
913971
};

css/style.css

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1000,3 +1000,10 @@ td#copyCode div {
10001000
#smaple-menu-content div:hover i:hover{
10011001
color:#f80;
10021002
}
1003+
1004+
#demo-frame {
1005+
width: 100%;
1006+
height: calc(100% - 120px);
1007+
border: none;
1008+
z-index: 2;
1009+
}

index.html

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
<meta charset="utf-8">
66
<meta name="google" value="notranslate">
77
<title>Webduino Blockly 線上編輯工具</title>
8-
<meta name="viewport" content="width=720, initial-scale=1" />
8+
<meta name="viewport" content="width=720, initial-scale=1, user-scalable=no"/>
99
<meta itemprop="name" content="Webduino Blockly 線上編輯工具">
1010
<meta itemprop="image" content="//blockly.webduino.io/media/snapshot.jpg">
1111
<meta itemprop="description" content="Webduino Blockly 線上編輯工具,提供簡單便利的編輯模式,同時具備了完整豐富的線上影音教學,在程式積木拖拉之餘,還可以產生標準的 Javascript 進行後續的應用開發,大幅降低學習門檻,輕鬆玩轉 Arduino 以及相關物聯網的應用。">
@@ -20,6 +20,8 @@
2020
<link rel="stylesheet" href="css/style.css">
2121
<link rel="stylesheet" href="css/blockly.css">
2222
<script src="storage.js"></script>
23+
<script src="launcher.js"></script>
24+
<script src="components/webcomponentsjs/webcomponents-lite.min.js"></script>
2325
<script src="components/blockly-src/blockly_compressed.js"></script>
2426
<script src="components/blockly-src/blocks_compressed.js"></script>
2527
<script src="components/blockly-src/javascript_compressed.js"></script>

0 commit comments

Comments
 (0)