chore(demos): delete some duplicated demos (#6066)
* chore(demos): delete codelab folder * chore(demos): delete custom dialog demo * chore(demos): delete custom fields demos * chore(demos): delete fixed injection demo * chore(demos): delete generator demo * chore(demos): delete interpreter demos * chore(demos): delete max blocks demo * chore(demos): delete graph demo * chore(demos): remove deleted demos from index.html
@@ -1,16 +0,0 @@
|
||||
# Blockly for the Web codelab
|
||||
|
||||
Code for the [Blockly for the Web codelab](https://developers.google.com/TODO).
|
||||
|
||||
In this codelab, you'll learn how to use Blockly JavaScript library
|
||||
to add a block code editor to a web application.
|
||||
|
||||
## What you'll learn
|
||||
|
||||
* How to add Blockly to a sample web app.
|
||||
* How to set up a Blockly workspace.
|
||||
* How to create a new block in Blockly.
|
||||
* How to generate and run JavaScript code from blocks.
|
||||
|
||||
Example code for each step of the codelab is available from
|
||||
the [completed](completed/) directory.
|
||||
@@ -1,73 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Blockly for the Web Codelab</title>
|
||||
|
||||
<link rel="stylesheet" href="https://code.getmdl.io/1.2.1/material.indigo-pink.min.css">
|
||||
<link rel="stylesheet" href="styles/index.css">
|
||||
</head>
|
||||
|
||||
<body mode="maker">
|
||||
<header class="mdl-color--cyan-500">
|
||||
<h1 class="mode-maker">Music Maker</h1>
|
||||
<h1 class="mode-edit mode-blockly">Music Maker Configuration</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<button class="mode-maker mdl-button" id="edit">Edit</button>
|
||||
<button class="mode-edit mdl-button mdl-js-button" id="done">Done</button>
|
||||
<button class="mode-blockly mdl-button mdl-js-button" id="save">Save</button>
|
||||
<p class="hint mode-edit">Tap any button to edit its code. <br/>When complete, press Done.</p>
|
||||
|
||||
<div class="maker">
|
||||
<div>
|
||||
<div class="button mdl-color--amber-500">1</div>
|
||||
<div class="button mdl-color--yellow-500">2</div>
|
||||
<div class="button mdl-color--lime-500">3</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="button mdl-color--pink-500">4</div>
|
||||
<div class="button mdl-color--red-500">5</div>
|
||||
<div class="button mdl-color--light-green-500">6</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="button mdl-color--cyan-500">7</div>
|
||||
<div class="button mdl-color--teal-500">8</div>
|
||||
<div class="button mdl-color--green-500">9</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="blockly-editor">
|
||||
<div id="blockly-div" style="height: 480px; width: 400px;"></div>
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<category name="Loops" colour="120">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">5</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Sounds" colour="355">
|
||||
<block type="play_sound"></block>
|
||||
</category>
|
||||
</xml>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="../../../blockly_compressed.js"></script>
|
||||
<script src="../../../blocks_compressed.js"></script>
|
||||
<script src="../../../javascript_compressed.js"></script>
|
||||
<script src="../../../msg/js/en.js"></script>
|
||||
|
||||
<script src="scripts/music_maker.js"></script>
|
||||
<script src="scripts/sound_blocks.js"></script>
|
||||
<script src="scripts/main.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,77 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
(function() {
|
||||
|
||||
let currentButton;
|
||||
|
||||
function handlePlay(event) {
|
||||
loadWorkspace(event.target);
|
||||
Blockly.JavaScript.addReservedWords('code');
|
||||
var code = Blockly.JavaScript.workspaceToCode(Blockly.getMainWorkspace());
|
||||
code += 'MusicMaker.play();';
|
||||
// Eval can be dangerous. For more controlled execution, check
|
||||
// https://github.com/NeilFraser/JS-Interpreter.
|
||||
try {
|
||||
eval(code);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
}
|
||||
}
|
||||
|
||||
function loadWorkspace(button) {
|
||||
let workspace = Blockly.getMainWorkspace();
|
||||
workspace.clear();
|
||||
if (button.blocklyXml) {
|
||||
Blockly.Xml.domToWorkspace(button.blocklyXml, workspace);
|
||||
}
|
||||
}
|
||||
|
||||
function save(button) {
|
||||
let xml = Blockly.Xml.workspaceToDom(Blockly.getMainWorkspace());
|
||||
button.blocklyXml = xml;
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
document.body.setAttribute('mode', 'edit');
|
||||
save(currentButton);
|
||||
}
|
||||
|
||||
function enableEditMode() {
|
||||
document.body.setAttribute('mode', 'edit');
|
||||
document.querySelectorAll('.button').forEach(btn => {
|
||||
btn.removeEventListener('click', handlePlay);
|
||||
btn.addEventListener('click', enableBlocklyMode);
|
||||
});
|
||||
}
|
||||
|
||||
function enableMakerMode() {
|
||||
document.body.setAttribute('mode', 'maker');
|
||||
document.querySelectorAll('.button').forEach(btn => {
|
||||
btn.addEventListener('click', handlePlay);
|
||||
btn.removeEventListener('click', enableBlocklyMode);
|
||||
});
|
||||
}
|
||||
|
||||
function enableBlocklyMode(e) {
|
||||
document.body.setAttribute('mode', 'blockly');
|
||||
currentButton = e.target;
|
||||
loadWorkspace(currentButton);
|
||||
}
|
||||
|
||||
document.querySelector('#edit').addEventListener('click', enableEditMode);
|
||||
document.querySelector('#done').addEventListener('click', enableMakerMode);
|
||||
document.querySelector('#save').addEventListener('click', handleSave);
|
||||
|
||||
enableMakerMode();
|
||||
|
||||
Blockly.inject('blockly-div', {
|
||||
media: '../../../media/',
|
||||
toolbox: document.getElementById('toolbox'),
|
||||
toolboxPosition: 'end',
|
||||
horizontalLayout: true,
|
||||
scrollbars: false
|
||||
});
|
||||
})();
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
const MusicMaker = {
|
||||
queue_: [],
|
||||
player_: new Audio(),
|
||||
queueSound: function(soundUrl) {
|
||||
this.queue_.push(soundUrl);
|
||||
},
|
||||
play: function() {
|
||||
let next = this.queue_.shift();
|
||||
if (next) {
|
||||
this.player_.src = next;
|
||||
this.player_.play();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
MusicMaker.player_.addEventListener(
|
||||
'ended', MusicMaker.play.bind(MusicMaker));
|
||||
@@ -1,39 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
Blockly.defineBlocksWithJsonArray([
|
||||
// Block for colour picker.
|
||||
{
|
||||
"type": "play_sound",
|
||||
"message0": "Play %1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "field_dropdown",
|
||||
"name": "VALUE",
|
||||
"options": [
|
||||
["C4", "sounds/c4.m4a"],
|
||||
["D4", "sounds/d4.m4a"],
|
||||
["E4", "sounds/e4.m4a"],
|
||||
["F4", "sounds/f4.m4a"],
|
||||
["G4", "sounds/g4.m4a"],
|
||||
["A5", "sounds/a5.m4a"],
|
||||
["B5", "sounds/b5.m4a"],
|
||||
["C5", "sounds/c5.m4a"]
|
||||
]
|
||||
}
|
||||
],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": 355,
|
||||
"tooltip": "",
|
||||
"helpUrl": ""
|
||||
}
|
||||
]);
|
||||
|
||||
Blockly.JavaScript['play_sound'] = function(block) {
|
||||
var value = '\'' + block.getFieldValue('VALUE') + '\'';
|
||||
return 'MusicMaker.queueSound(' + value + ');\n';
|
||||
};
|
||||
@@ -1,75 +0,0 @@
|
||||
main {
|
||||
width: 400px;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
overflow:hidden;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: green;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
width: 400px;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
color: #fff;
|
||||
font-size: 1.8em;
|
||||
line-height: 2.4em;
|
||||
}
|
||||
|
||||
.mode-edit,
|
||||
.mode-maker,
|
||||
.mode-blockly {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[mode="maker"] .mode-maker,
|
||||
[mode="edit"] .mode-edit,
|
||||
[mode="blockly"] .mode-blockly {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.blockly-editor {
|
||||
position: absolute;
|
||||
top: 64px;
|
||||
left: -400px;
|
||||
transition: left .4s;
|
||||
height: 460px;
|
||||
width: 400px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
[mode="blockly"] .blockly-editor {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.maker {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: space-between;
|
||||
height: 460px;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.maker > div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 120px;
|
||||
height: 140px;
|
||||
color: #fff;
|
||||
font-size: 3em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 140px;
|
||||
}
|
||||
|
||||
.mdl-button {
|
||||
margin: 1em 0;
|
||||
float: right;
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
||||
<title>Blockly for the Web Codelab</title>
|
||||
|
||||
<link rel="stylesheet" href="https://code.getmdl.io/1.2.1/material.indigo-pink.min.css">
|
||||
<link rel="stylesheet" href="styles/index.css">
|
||||
</head>
|
||||
|
||||
<body mode="maker">
|
||||
<header class="mdl-color--cyan-500">
|
||||
<h1 class="mode-maker">Music Maker</h1>
|
||||
<h1 class="mode-edit mode-blockly">Music Maker Configuration</h1>
|
||||
</header>
|
||||
|
||||
<main>
|
||||
<button class="mode-maker mdl-button" id="edit">Edit</button>
|
||||
<button class="mode-edit mdl-button mdl-js-button" id="done">Done</button>
|
||||
<button class="mode-blockly mdl-button mdl-js-button" id="save">Save</button>
|
||||
<p class="hint mode-edit">Tap any button to edit its code. <br/>When complete, press Done.</p>
|
||||
|
||||
<div class="maker">
|
||||
<div>
|
||||
<div class="button mdl-color--amber-500">1</div>
|
||||
<div class="button mdl-color--yellow-500">2</div>
|
||||
<div class="button mdl-color--lime-500">3</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="button mdl-color--pink-500">4</div>
|
||||
<div class="button mdl-color--red-500">5</div>
|
||||
<div class="button mdl-color--light-green-500">6</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="button mdl-color--cyan-500">7</div>
|
||||
<div class="button mdl-color--teal-500">8</div>
|
||||
<div class="button mdl-color--green-500">9</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="blockly-editor">
|
||||
<div id="blockly-div" style="height: 480px; width: 400px;"></div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<script src="scripts/music_maker.js"></script>
|
||||
<script src="scripts/main.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,50 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
(function() {
|
||||
|
||||
let currentButton;
|
||||
|
||||
function handlePlay(event) {
|
||||
// Add code for playing sound.
|
||||
}
|
||||
|
||||
function save(button) {
|
||||
// Add code for saving the behavior of a button.
|
||||
}
|
||||
|
||||
function handleSave() {
|
||||
document.body.setAttribute('mode', 'edit');
|
||||
save(currentButton);
|
||||
}
|
||||
|
||||
function enableEditMode() {
|
||||
document.body.setAttribute('mode', 'edit');
|
||||
document.querySelectorAll('.button').forEach(btn => {
|
||||
btn.removeEventListener('click', handlePlay);
|
||||
btn.addEventListener('click', enableBlocklyMode);
|
||||
});
|
||||
}
|
||||
|
||||
function enableMakerMode() {
|
||||
document.body.setAttribute('mode', 'maker');
|
||||
document.querySelectorAll('.button').forEach(btn => {
|
||||
btn.addEventListener('click', handlePlay);
|
||||
btn.removeEventListener('click', enableBlocklyMode);
|
||||
});
|
||||
}
|
||||
|
||||
function enableBlocklyMode(e) {
|
||||
document.body.setAttribute('mode', 'blockly');
|
||||
currentButton = e.target;
|
||||
}
|
||||
|
||||
document.querySelector('#edit').addEventListener('click', enableEditMode);
|
||||
document.querySelector('#done').addEventListener('click', enableMakerMode);
|
||||
document.querySelector('#save').addEventListener('click', handleSave);
|
||||
|
||||
enableMakerMode();
|
||||
|
||||
})();
|
||||
@@ -1,22 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
const MusicMaker = {
|
||||
queue_: [],
|
||||
player_: new Audio(),
|
||||
queueSound: function(soundUrl) {
|
||||
this.queue_.push(soundUrl);
|
||||
},
|
||||
play: function() {
|
||||
let next = this.queue_.shift();
|
||||
if (next) {
|
||||
this.player_.src = next;
|
||||
this.player_.play();
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
MusicMaker.player_.addEventListener(
|
||||
'ended', MusicMaker.play.bind(MusicMaker));
|
||||
@@ -1,75 +0,0 @@
|
||||
main {
|
||||
width: 400px;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
overflow:hidden;
|
||||
height: 600px;
|
||||
}
|
||||
|
||||
header {
|
||||
background-color: green;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
h1 {
|
||||
width: 400px;
|
||||
position: relative;
|
||||
margin: 0 auto;
|
||||
color: #fff;
|
||||
font-size: 1.8em;
|
||||
line-height: 2.4em;
|
||||
}
|
||||
|
||||
.mode-edit,
|
||||
.mode-maker,
|
||||
.mode-blockly {
|
||||
display: none;
|
||||
}
|
||||
|
||||
[mode="maker"] .mode-maker,
|
||||
[mode="edit"] .mode-edit,
|
||||
[mode="blockly"] .mode-blockly {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.blockly-editor {
|
||||
position: absolute;
|
||||
top: 64px;
|
||||
left: -400px;
|
||||
transition: left .4s;
|
||||
height: 460px;
|
||||
width: 400px;
|
||||
background-color: #eee;
|
||||
}
|
||||
|
||||
[mode="blockly"] .blockly-editor {
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.maker {
|
||||
display: flex;
|
||||
flex-flow: column;
|
||||
justify-content: space-between;
|
||||
height: 460px;
|
||||
width: 400px;
|
||||
}
|
||||
|
||||
.maker > div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.button {
|
||||
width: 120px;
|
||||
height: 140px;
|
||||
color: #fff;
|
||||
font-size: 3em;
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
line-height: 140px;
|
||||
}
|
||||
|
||||
.mdl-button {
|
||||
margin: 1em 0;
|
||||
float: right;
|
||||
}
|
||||
@@ -1,161 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* An example implementation of how one might replace Blockly's browser
|
||||
* dialogs. This is just an example, and applications are not encouraged to use
|
||||
* it verbatim.
|
||||
*
|
||||
* @namespace
|
||||
*/
|
||||
CustomDialog = {};
|
||||
|
||||
/** Override Blockly.dialog.alert() with custom implementation. */
|
||||
Blockly.dialog.setAlert(function(message, callback) {
|
||||
console.log('Alert: ' + message);
|
||||
CustomDialog.show('Alert', message, {
|
||||
onCancel: callback
|
||||
});
|
||||
});
|
||||
|
||||
/** Override Blockly.dialog.confirm() with custom implementation. */
|
||||
Blockly.dialog.setConfirm(function(message, callback) {
|
||||
console.log('Confirm: ' + message);
|
||||
CustomDialog.show('Confirm', message, {
|
||||
showOkay: true,
|
||||
onOkay: function() {
|
||||
callback(true);
|
||||
},
|
||||
showCancel: true,
|
||||
onCancel: function() {
|
||||
callback(false);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
/** Override Blockly.dialog.prompt() with custom implementation. */
|
||||
Blockly.dialog.setPrompt(function(message, defaultValue, callback) {
|
||||
console.log('Prompt: ' + message);
|
||||
CustomDialog.show('Prompt', message, {
|
||||
showInput: true,
|
||||
showOkay: true,
|
||||
onOkay: function() {
|
||||
callback(CustomDialog.inputField.value);
|
||||
},
|
||||
showCancel: true,
|
||||
onCancel: function() {
|
||||
callback(null);
|
||||
}
|
||||
});
|
||||
CustomDialog.inputField.value = defaultValue;
|
||||
});
|
||||
|
||||
/** Hides any currently visible dialog. */
|
||||
CustomDialog.hide = function() {
|
||||
if (CustomDialog.backdropDiv_) {
|
||||
CustomDialog.backdropDiv_.style.display = 'none';
|
||||
CustomDialog.dialogDiv_.style.display = 'none';
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Shows the dialog.
|
||||
* Allowed options:
|
||||
* - showOkay: Whether to show the OK button.
|
||||
* - showCancel: Whether to show the Cancel button.
|
||||
* - showInput: Whether to show the text input field.
|
||||
* - onOkay: Callback to handle the okay button.
|
||||
* - onCancel: Callback to handle the cancel button and backdrop clicks.
|
||||
*/
|
||||
CustomDialog.show = function(title, message, options) {
|
||||
var backdropDiv = CustomDialog.backdropDiv_;
|
||||
var dialogDiv = CustomDialog.dialogDiv_;
|
||||
if (!dialogDiv) {
|
||||
// Generate HTML
|
||||
backdropDiv = document.createElement('div');
|
||||
backdropDiv.id = 'customDialogBackdrop';
|
||||
backdropDiv.style.cssText =
|
||||
'position: absolute;' +
|
||||
'top: 0; left: 0; right: 0; bottom: 0;' +
|
||||
'background-color: rgba(0, 0, 0, .7);' +
|
||||
'z-index: 100;';
|
||||
document.body.appendChild(backdropDiv);
|
||||
|
||||
dialogDiv = document.createElement('div');
|
||||
dialogDiv.id = 'customDialog';
|
||||
dialogDiv.style.cssText =
|
||||
'background-color: #fff;' +
|
||||
'width: 400px;' +
|
||||
'margin: 20px auto 0;' +
|
||||
'padding: 10px;';
|
||||
backdropDiv.appendChild(dialogDiv);
|
||||
|
||||
dialogDiv.onclick = function(event) {
|
||||
event.stopPropagation();
|
||||
};
|
||||
|
||||
CustomDialog.backdropDiv_ = backdropDiv;
|
||||
CustomDialog.dialogDiv_ = dialogDiv;
|
||||
}
|
||||
backdropDiv.style.display = 'block';
|
||||
dialogDiv.style.display = 'block';
|
||||
|
||||
dialogDiv.innerHTML =
|
||||
'<header class="customDialogTitle"></header>' +
|
||||
'<p class="customDialogMessage"></p>' +
|
||||
(options.showInput ? '<div><input id="customDialogInput"></div>' : '') +
|
||||
'<div class="customDialogButtons">' +
|
||||
(options.showCancel ? '<button id="customDialogCancel">Cancel</button>': '') +
|
||||
(options.showOkay ? '<button id="customDialogOkay">OK</button>': '') +
|
||||
'</div>';
|
||||
dialogDiv.getElementsByClassName('customDialogTitle')[0]
|
||||
.appendChild(document.createTextNode(title));
|
||||
dialogDiv.getElementsByClassName('customDialogMessage')[0]
|
||||
.appendChild(document.createTextNode(message));
|
||||
|
||||
var onOkay = function(event) {
|
||||
CustomDialog.hide();
|
||||
options.onOkay && options.onOkay();
|
||||
event && event.stopPropagation();
|
||||
};
|
||||
var onCancel = function(event) {
|
||||
CustomDialog.hide();
|
||||
options.onCancel && options.onCancel();
|
||||
event && event.stopPropagation();
|
||||
};
|
||||
|
||||
var dialogInput = document.getElementById('customDialogInput');
|
||||
CustomDialog.inputField = dialogInput;
|
||||
if (dialogInput) {
|
||||
dialogInput.focus();
|
||||
|
||||
dialogInput.onkeyup = function(event) {
|
||||
if (event.keyCode === 13) {
|
||||
// Process as OK when user hits enter.
|
||||
onOkay();
|
||||
return false;
|
||||
} else if (event.keyCode === 27) {
|
||||
// Process as cancel when user hits esc.
|
||||
onCancel();
|
||||
return false;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
var okay = document.getElementById('customDialogOkay');
|
||||
okay && okay.focus();
|
||||
}
|
||||
|
||||
if (options.showOkay) {
|
||||
document.getElementById('customDialogOkay')
|
||||
.addEventListener('click', onOkay);
|
||||
}
|
||||
if (options.showCancel) {
|
||||
document.getElementById('customDialogCancel')
|
||||
.addEventListener('click', onCancel);
|
||||
}
|
||||
|
||||
backdropDiv.onclick = onCancel;
|
||||
};
|
||||
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -1,62 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Custom Dialog</title>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Custom Dialog</h1>
|
||||
|
||||
<p>This is a simple demo of replacing modal browser dialogs with HTML.
|
||||
To see how it works, see the source code in
|
||||
<a href="custom-dialog.js">custom-dialog.js</a>
|
||||
</p>
|
||||
|
||||
<p>Try creating new variables, creating variables with names already in
|
||||
use, or deleting multiple blocks on the workspace.
|
||||
</p>
|
||||
|
||||
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<category name="Inputs" colour="%{BKY_MATH_HUE}">
|
||||
<block type="math_number" gap="32">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
<block type="text"></block>
|
||||
<block type="text_prompt_ext">
|
||||
<value name="TEXT">
|
||||
<shadow type="text">
|
||||
<field name="TEXT">abc</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<sep></sep>
|
||||
<category name="Variables" colour="%{BKY_VARIABLES_HUE}" custom="VARIABLE"></category>
|
||||
<category name="Functions" colour="%{BKY_PROCEDURES_HUE}" custom="PROCEDURE"></category>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var demoWorkspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
</script>
|
||||
<script src="custom-dialog.js"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,54 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Custom Fields</title>
|
||||
<style>
|
||||
body {
|
||||
margin: 0 10%;
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
td {
|
||||
padding: 1ex;
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Custom Fields</h1>
|
||||
|
||||
<p>These demos are intended for developers who want creating custom block fields.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="turtle/index.html">
|
||||
<img src="turtle/icon.png" height=80 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="turtle/index.html">Turtle Field</a></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="pitch/index.html">
|
||||
<img src="pitch/media/notes.png" height=40 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="pitch/index.html">Pitch Field</a></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,18 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Pitch field demo blocks.
|
||||
*/
|
||||
|
||||
Blockly.Blocks['test_pitch_field'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField('pitch')
|
||||
.appendField(new CustomFields.FieldPitch('7'), 'PITCH');
|
||||
this.setStyle('loop_blocks');
|
||||
}
|
||||
};
|
||||
@@ -1,235 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2016 Google LLC
|
||||
* https://github.com/google/blockly-games
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Music pitch input field. Borrowed from Blockly Games.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('CustomFields.FieldPitch');
|
||||
|
||||
goog.require('Blockly.FieldTextInput');
|
||||
goog.require('Blockly.utils.math');
|
||||
goog.require('Blockly.utils.object');
|
||||
|
||||
var CustomFields = CustomFields || {};
|
||||
|
||||
/**
|
||||
* Class for an editable pitch field.
|
||||
* @param {string} text The initial content of the field.
|
||||
* @extends {Blockly.FieldTextInput}
|
||||
* @constructor
|
||||
*/
|
||||
CustomFields.FieldPitch = function(text) {
|
||||
CustomFields.FieldPitch.superClass_.constructor.call(this, text);
|
||||
|
||||
/**
|
||||
* Click event data.
|
||||
* @type {?Blockly.browserEvents.Data}
|
||||
* @private
|
||||
*/
|
||||
this.clickWrapper_ = null;
|
||||
|
||||
/**
|
||||
* Move event data.
|
||||
* @type {?Blockly.browserEvents.Data}
|
||||
* @private
|
||||
*/
|
||||
this.moveWrapper_ = null;
|
||||
};
|
||||
Blockly.utils.object.inherits(CustomFields.FieldPitch, Blockly.FieldTextInput);
|
||||
|
||||
/**
|
||||
* Construct a FieldPitch from a JSON arg object.
|
||||
* @param {!Object} options A JSON object with options (pitch).
|
||||
* @return {!CustomFields.FieldPitch} The new field instance.
|
||||
* @package
|
||||
* @nocollapse
|
||||
*/
|
||||
CustomFields.FieldPitch.fromJson = function(options) {
|
||||
return new CustomFields.FieldPitch(options['pitch']);
|
||||
};
|
||||
|
||||
/**
|
||||
* All notes available for the picker.
|
||||
*/
|
||||
CustomFields.FieldPitch.NOTES = 'C3 D3 E3 F3 G3 A3 B3 C4 D4 E4 F4 G4 A4'.split(/ /);
|
||||
|
||||
/**
|
||||
* Show the inline free-text editor on top of the text and the note picker.
|
||||
* @protected
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.showEditor_ = function() {
|
||||
CustomFields.FieldPitch.superClass_.showEditor_.call(this);
|
||||
|
||||
var div = Blockly.WidgetDiv.getDiv();
|
||||
if (!div.firstChild) {
|
||||
// Mobile interface uses Blockly.dialog.prompt.
|
||||
return;
|
||||
}
|
||||
// Build the DOM.
|
||||
var editor = this.dropdownCreate_();
|
||||
Blockly.DropDownDiv.getContentDiv().appendChild(editor);
|
||||
|
||||
Blockly.DropDownDiv.setColour(this.sourceBlock_.style.colourPrimary,
|
||||
this.sourceBlock_.style.colourTertiary);
|
||||
|
||||
Blockly.DropDownDiv.showPositionedByField(
|
||||
this, this.dropdownDispose_.bind(this));
|
||||
|
||||
// The note picker is different from other fields in that it updates on
|
||||
// mousemove even if it's not in the middle of a drag. In future we may
|
||||
// change this behaviour. For now, using bindEvent_ instead of
|
||||
// bindEventWithChecks_ allows it to work without a mousedown/touchstart.
|
||||
this.clickWrapper_ =
|
||||
Blockly.browserEvents.bind(this.imageElement_, 'click', this, this.hide_);
|
||||
this.moveWrapper_ = Blockly.browserEvents.bind(
|
||||
this.imageElement_, 'mousemove', this, this.onMouseMove);
|
||||
|
||||
this.updateGraph_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Create the pitch editor.
|
||||
* @return {!Element} The newly created pitch picker.
|
||||
* @private
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.dropdownCreate_ = function() {
|
||||
this.imageElement_ = document.createElement('div');
|
||||
this.imageElement_.id = 'notePicker';
|
||||
|
||||
return this.imageElement_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Dispose of events belonging to the pitch editor.
|
||||
* @private
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.dropdownDispose_ = function() {
|
||||
if (this.clickWrapper_) {
|
||||
Blockly.browserEvents.unbind(this.clickWrapper_);
|
||||
this.clickWrapper_ = null;
|
||||
}
|
||||
if (this.moveWrapper_) {
|
||||
Blockly.browserEvents.unbind(this.moveWrapper_);
|
||||
this.moveWrapper_ = null;
|
||||
}
|
||||
this.imageElement_ = null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Hide the editor.
|
||||
* @private
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.hide_ = function() {
|
||||
Blockly.WidgetDiv.hide();
|
||||
Blockly.DropDownDiv.hideWithoutAnimation();
|
||||
};
|
||||
|
||||
/**
|
||||
* Set the note to match the mouse's position.
|
||||
* @param {!Event} e Mouse move event.
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.onMouseMove = function(e) {
|
||||
var bBox = this.imageElement_.getBoundingClientRect();
|
||||
var dy = e.clientY - bBox.top;
|
||||
var note = Blockly.utils.math.clamp(Math.round(13.5 - dy / 7.5), 0, 12);
|
||||
this.imageElement_.style.backgroundPosition = (-note * 37) + 'px 0';
|
||||
this.setEditorValue_(note);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the machine-readable value (0-12) to human-readable text (C3-A4).
|
||||
* @param {number|string} value The provided value.
|
||||
* @return {string|undefined} The respective note, or undefined if invalid.
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.valueToNote = function(value) {
|
||||
return CustomFields.FieldPitch.NOTES[Number(value)];
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert the human-readable text (C3-A4) to machine-readable value (0-12).
|
||||
* @param {string} text The provided note.
|
||||
* @return {number|undefined} The respective value, or undefined if invalid.
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.noteToValue = function(text) {
|
||||
var normalizedText = text.trim().toUpperCase();
|
||||
var i = CustomFields.FieldPitch.NOTES.indexOf(normalizedText);
|
||||
return i > -1 ? i : undefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get the text to be displayed on the field node.
|
||||
* @return {?string} The HTML value if we're editing, otherwise null. Null means
|
||||
* the super class will handle it, likely a string cast of value.
|
||||
* @protected
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.getText_ = function() {
|
||||
if (this.isBeingEdited_) {
|
||||
return CustomFields.FieldPitch.superClass_.getText_.call(this);
|
||||
}
|
||||
return this.valueToNote(this.getValue()) || null;
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform the provided value into a text to show in the HTML input.
|
||||
* @param {*} value The value stored in this field.
|
||||
* @return {string} The text to show on the HTML input.
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.getEditorText_ = function(value) {
|
||||
return this.valueToNote(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Transform the text received from the HTML input (note) into a value
|
||||
* to store in this field.
|
||||
* @param {string} text Text received from the HTML input.
|
||||
* @return {*} The value to store.
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.getValueFromEditorText_ = function(text) {
|
||||
return this.noteToValue(text);
|
||||
};
|
||||
|
||||
/**
|
||||
* Updates the graph when the field rerenders.
|
||||
* @private
|
||||
* @override
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.render_ = function() {
|
||||
CustomFields.FieldPitch.superClass_.render_.call(this);
|
||||
this.updateGraph_();
|
||||
};
|
||||
|
||||
/**
|
||||
* Redraw the note picker with the current note.
|
||||
* @private
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.updateGraph_ = function() {
|
||||
if (!this.imageElement_) {
|
||||
return;
|
||||
}
|
||||
var i = this.getValue();
|
||||
this.imageElement_.style.backgroundPosition = (-i * 37) + 'px 0';
|
||||
};
|
||||
|
||||
/**
|
||||
* Ensure that only a valid value may be entered.
|
||||
* @param {*} opt_newValue The input value.
|
||||
* @return {*} A valid value, or null if invalid.
|
||||
*/
|
||||
CustomFields.FieldPitch.prototype.doClassValidation_ = function(opt_newValue) {
|
||||
if (opt_newValue === null || opt_newValue === undefined) {
|
||||
return null;
|
||||
}
|
||||
var note = this.valueToNote(opt_newValue);
|
||||
if (note) {
|
||||
return opt_newValue;
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
Blockly.fieldRegistry.register('field_pitch', CustomFields.FieldPitch);
|
||||
@@ -1,118 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Blockly Demo: Custom Pitch Field</title>
|
||||
<script src="../../../blockly_uncompressed.js"></script>
|
||||
<script src="blocks.js"></script>
|
||||
<script src="field_pitch.js"></script>
|
||||
<script src="../../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0 10%;
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
td {
|
||||
padding: 1ex;
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="pitch.css">
|
||||
</head>
|
||||
<body onload="start()">
|
||||
<h1>
|
||||
<a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../../index.html">Demos</a> >
|
||||
<a href="../index.html">Custom Fields</a> > Pitch Field</h1>
|
||||
|
||||
|
||||
<p>This is a demo of creating custom block fields. In this case the field
|
||||
is used to select a note pitch.
|
||||
</p>
|
||||
|
||||
<p>All of the custom field implementation is in
|
||||
demos/custom-fields/pitch/field_pitch.js, including comments on each required
|
||||
function.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Export to XML" onclick="toXml()">
|
||||
<input type="button" value="Import from XML" onclick="fromXml()">
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<textarea id="importExport"
|
||||
style="width: 200px; height: 480px;"></textarea>
|
||||
</td>
|
||||
<td>
|
||||
<div id="blocklyDiv" style="width: 600px; height: 480px;"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function toXml() {
|
||||
var output = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
output.value = Blockly.Xml.domToPrettyText(xml);
|
||||
output.focus();
|
||||
output.select();
|
||||
}
|
||||
|
||||
function fromXml() {
|
||||
var input = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.textToDom(input.value);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
}
|
||||
|
||||
function appendDom() {
|
||||
var blocks = document.getElementById('workspace-blocks');
|
||||
if (blocks.firstElementChild) {
|
||||
Blockly.Xml.appendDomToWorkspace(blocks, workspace);
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
workspace = Blockly.inject('blocklyDiv', options);
|
||||
appendDom();
|
||||
workspace.scrollCenter();
|
||||
}
|
||||
|
||||
var options = {
|
||||
media: '../../../media/',
|
||||
grid: {
|
||||
spacing: 25,
|
||||
length: 3,
|
||||
colour: '#ccc'
|
||||
},
|
||||
move: {
|
||||
scrollbars: true,
|
||||
drag: true,
|
||||
wheel: true,
|
||||
},
|
||||
zoom: {
|
||||
controls: true,
|
||||
startScale: 1.0,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
}
|
||||
/*toolbox: document.getElementById('toolbox')*/
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="workspace-blocks" style="display: none">
|
||||
<block type="test_pitch_field"></block>
|
||||
</xml>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 894 B |
@@ -1,13 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
|
||||
#notePicker {
|
||||
background-image: url(media/notes.png);
|
||||
border: 1px solid #ccc;
|
||||
height: 109px;
|
||||
width: 46px;
|
||||
}
|
||||
@@ -1,91 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Turtle field demo blocks.
|
||||
*/
|
||||
|
||||
Blockly.Blocks['turtle_basic'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField('simple turtle');
|
||||
this.appendDummyInput()
|
||||
.setAlign(Blockly.ALIGN_CENTRE)
|
||||
.appendField(new CustomFields.FieldTurtle(), 'TURTLE');
|
||||
this.setStyle('loop_blocks');
|
||||
this.setCommentText('Demonstrates a turtle field with no validator.');
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['turtle_nullifier'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.appendField('no trademarks');
|
||||
this.appendDummyInput()
|
||||
.setAlign(Blockly.ALIGN_CENTRE)
|
||||
.appendField(new CustomFields.FieldTurtle(null, null, null, this.validate)
|
||||
, 'TURTLE');
|
||||
this.setStyle('loop_blocks');
|
||||
this.setCommentText('Validates combinations of names and hats to null' +
|
||||
' (invalid) if they could be considered infringe-y. This turns the' +
|
||||
' turtle field red. Infringe-y combinations are: (Leonardo, Mask),' +
|
||||
' (Yertle, Crown), and (Franklin, Propeller).');
|
||||
},
|
||||
|
||||
validate: function(newValue) {
|
||||
this.cachedValidatedValue_ = {
|
||||
turtleName: newValue.turtleName,
|
||||
pattern: newValue.pattern,
|
||||
hat: newValue.hat,
|
||||
};
|
||||
if ((newValue.turtleName === 'Leonardo' && newValue.hat === 'Mask') ||
|
||||
(newValue.turtleName === 'Yertle' && newValue.hat === 'Crown') ||
|
||||
(newValue.turtleName === 'Franklin') && newValue.hat === 'Propeller') {
|
||||
|
||||
var currentValue = this.getValue();
|
||||
if (newValue.turtleName !== currentValue.turtleName) {
|
||||
// Turtle name changed.
|
||||
this.cachedValidatedValue_.turtleName = null;
|
||||
} else {
|
||||
// Hat must have changed.
|
||||
this.cachedValidatedValue_.hat = null;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Blocks['turtle_changer'] = {
|
||||
init: function() {
|
||||
this.appendDummyInput()
|
||||
.setAlign(Blockly.ALIGN_CENTRE)
|
||||
.appendField('force hats');
|
||||
this.appendDummyInput()
|
||||
.appendField(new CustomFields.FieldTurtle(
|
||||
'Dots', 'Crown', 'Yertle', this.validate), 'TURTLE');
|
||||
this.setStyle('loop_blocks');
|
||||
this.setCommentText('Validates the input so that certain names always' +
|
||||
' have specific hats. The name-hat combinations are: (Leonardo, Mask),' +
|
||||
' (Yertle, Crown), (Franklin, Propeller).');
|
||||
},
|
||||
|
||||
validate: function(newValue) {
|
||||
switch(newValue.turtleName) {
|
||||
case 'Leonardo':
|
||||
newValue.hat = 'Mask';
|
||||
break;
|
||||
case 'Yertle':
|
||||
newValue.hat = 'Crown';
|
||||
break;
|
||||
case 'Franklin':
|
||||
newValue.hat = 'Propeller';
|
||||
break;
|
||||
}
|
||||
return newValue;
|
||||
}
|
||||
};
|
||||
@@ -1,747 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview A field used to customize a turtle.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
// You must provide the constructor for your custom field.
|
||||
goog.provide('CustomFields.FieldTurtle');
|
||||
|
||||
// You must require the abstract field class to inherit from.
|
||||
goog.require('Blockly.Field');
|
||||
goog.require('Blockly.fieldRegistry');
|
||||
goog.require('Blockly.utils');
|
||||
goog.require('Blockly.utils.dom');
|
||||
goog.require('Blockly.utils.object');
|
||||
goog.require('Blockly.utils.Size');
|
||||
|
||||
var CustomFields = CustomFields || {};
|
||||
|
||||
// Generally field's values should be optional, and have logical defaults.
|
||||
// If this is not possible (for example image fields can't have logical
|
||||
// defaults) the field should throw a clear error when a value is not provided.
|
||||
// Editable fields also generally accept validators, so we will accept a
|
||||
// validator.
|
||||
CustomFields.FieldTurtle = function(
|
||||
opt_pattern, opt_hat, opt_turtleName, opt_validator) {
|
||||
|
||||
// The turtle field contains an object as its value, so we need to compile
|
||||
// the parameters into an object.
|
||||
var value = {};
|
||||
value.pattern = opt_pattern || CustomFields.FieldTurtle.PATTERNS[0];
|
||||
value.hat = opt_hat || CustomFields.FieldTurtle.HATS[0];
|
||||
value.turtleName = opt_turtleName || CustomFields.FieldTurtle.NAMES[0];
|
||||
|
||||
// A field constructor should always call its parent constructor, because
|
||||
// that helps keep the code organized and DRY.
|
||||
CustomFields.FieldTurtle.superClass_.constructor.call(
|
||||
this, value, opt_validator);
|
||||
|
||||
/**
|
||||
* The size of the area rendered by the field.
|
||||
* @type {Blockly.utils.Size}
|
||||
* @protected
|
||||
* @override
|
||||
*/
|
||||
this.size_ = new Blockly.utils.Size(0, 0);
|
||||
};
|
||||
Blockly.utils.object.inherits(CustomFields.FieldTurtle, Blockly.Field);
|
||||
|
||||
// This allows the field to be constructed using a JSON block definition.
|
||||
CustomFields.FieldTurtle.fromJson = function(options) {
|
||||
// In this case we simply pass the JSON options along to the constructor,
|
||||
// but you can also use this to get message references, and other such things.
|
||||
return new CustomFields.FieldTurtle(
|
||||
options['pattern'],
|
||||
options['hat'],
|
||||
options['turtleName']);
|
||||
};
|
||||
|
||||
// Since this field is editable we must also define serializable as true
|
||||
// (for backwards compatibility reasons serializable is false by default).
|
||||
CustomFields.FieldTurtle.prototype.SERIALIZABLE = true;
|
||||
|
||||
// The cursor property defines what the mouse will look like when the user
|
||||
// hovers over the field. By default the cursor will be whatever
|
||||
// .blocklyDraggable's cursor is defined as (vis. grab). Most fields define
|
||||
// this property as 'default'.
|
||||
CustomFields.FieldTurtle.prototype.CURSOR = 'pointer';
|
||||
|
||||
// How far to move the text to keep it to the right of the turtle.
|
||||
// May change if the turtle gets fancy enough.
|
||||
CustomFields.FieldTurtle.prototype.TEXT_OFFSET_X = 80;
|
||||
|
||||
// These are the different options for our turtle. Being declared this way
|
||||
// means they are static, and not translatable. If you want to do something
|
||||
// similar, but make it translatable you should set up your options like a
|
||||
// dropdown field, with language-neutral keys and human-readable values.
|
||||
CustomFields.FieldTurtle.PATTERNS =
|
||||
['Dots', 'Stripes', 'Hexagons'];
|
||||
CustomFields.FieldTurtle.HATS =
|
||||
['Stovepipe', 'Crown', 'Propeller', 'Mask', 'Fedora'];
|
||||
CustomFields.FieldTurtle.NAMES =
|
||||
['Yertle', 'Franklin', 'Crush', 'Leonardo', 'Bowser', 'Squirtle', 'Oogway'];
|
||||
|
||||
// Used to keep track of our editor event listeners, so they can be
|
||||
// properly disposed of when the field closes. You can keep track of your
|
||||
// listeners however you want, just be sure to dispose of them!
|
||||
CustomFields.FieldTurtle.prototype.editorListeners_ = [];
|
||||
|
||||
// Used to create the DOM of our field.
|
||||
CustomFields.FieldTurtle.prototype.initView = function() {
|
||||
// Because we want to have both a borderRect_ (background) and a
|
||||
// textElement_ (text) we can call the super-function. If we only wanted
|
||||
// one or the other, we could call their individual createX functions.
|
||||
CustomFields.FieldTurtle.superClass_.initView.call(this);
|
||||
|
||||
// Note that the field group is created by the abstract field's init_
|
||||
// function. This means that *all elements* should be children of the
|
||||
// fieldGroup_.
|
||||
this.createView_();
|
||||
};
|
||||
|
||||
// Updates how the field looks depending on if it is editable or not.
|
||||
CustomFields.FieldTurtle.prototype.updateEditable = function() {
|
||||
if (!this.fieldGroup_) {
|
||||
// Not initialized yet.
|
||||
return;
|
||||
}
|
||||
// The default functionality just makes it so the borderRect_ does not
|
||||
// highlight when hovered.
|
||||
Blockly.FieldColour.superClass_.updateEditable.call(this);
|
||||
// Things like this are best applied to the clickTarget_. By default the
|
||||
// click target is the same as getSvgRoot, which by default is the
|
||||
// fieldGroup_.
|
||||
var group = this.getClickTarget_();
|
||||
if (!this.isCurrentlyEditable()) {
|
||||
group.style.cursor = 'not-allowed';
|
||||
} else {
|
||||
group.style.cursor = this.CURSOR;
|
||||
}
|
||||
};
|
||||
|
||||
// Gets the text to display when the block is collapsed
|
||||
CustomFields.FieldTurtle.prototype.getText = function() {
|
||||
var text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
|
||||
if (this.value_.hat === 'Stovepipe' || this.value_.hat === 'Propeller') {
|
||||
text += ' hat';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
// Makes sure new field values (given to setValue) are valid, meaning
|
||||
// something this field can legally "hold". Class validators can either change
|
||||
// the input value, or return null if the input value is invalid. Called by
|
||||
// the setValue() function.
|
||||
CustomFields.FieldTurtle.prototype.doClassValidation_ = function(newValue) {
|
||||
// Undefined signals that we want the value to remain unchanged. This is a
|
||||
// special feature of turtle fields, but could be useful for other
|
||||
// multi-part fields.
|
||||
if (newValue.pattern === undefined) {
|
||||
newValue.pattern = this.displayValue_ && this.displayValue_.pattern;
|
||||
// We only want to allow patterns that are part of our pattern list.
|
||||
// Anything else is invalid, so we return null.
|
||||
} else if (CustomFields.FieldTurtle.PATTERNS.indexOf(newValue.pattern) === -1) {
|
||||
newValue.pattern = null;
|
||||
}
|
||||
|
||||
if (newValue.hat === undefined) {
|
||||
newValue.hat = this.displayValue_ && this.displayValue_.hat;
|
||||
} else if (CustomFields.FieldTurtle.HATS.indexOf(newValue.hat) === -1) {
|
||||
newValue.hat = null;
|
||||
}
|
||||
|
||||
if (newValue.turtleName === undefined) {
|
||||
newValue.turtleName = this.displayValue_ && this.displayValue_.turtleName;
|
||||
} else if (CustomFields.FieldTurtle.NAMES.indexOf(newValue.turtleName) === -1) {
|
||||
newValue.turtleName = null;
|
||||
}
|
||||
|
||||
// This is a strategy for dealing with defaults on multi-part values.
|
||||
// The class validator sets individual properties of the object to null
|
||||
// to indicate that they are invalid, and then caches that object to the
|
||||
// cachedValidatedValue_ property. This way the field can, for
|
||||
// example, properly handle an invalid pattern, combined with a valid hat.
|
||||
// This can also be done with local validators.
|
||||
this.cachedValidatedValue_ = newValue;
|
||||
|
||||
// Always be sure to return!
|
||||
if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
|
||||
return null;
|
||||
}
|
||||
return newValue;
|
||||
};
|
||||
|
||||
// Saves the new field value. Called by the setValue function.
|
||||
CustomFields.FieldTurtle.prototype.doValueUpdate_ = function(newValue) {
|
||||
// The default function sets this field's this.value_ property to the
|
||||
// newValue, and its this.isDirty_ property to true. The isDirty_ property
|
||||
// tells the setValue function whether the field needs to be re-rendered.
|
||||
CustomFields.FieldTurtle.superClass_.doValueUpdate_.call(this, newValue);
|
||||
this.displayValue_ = newValue;
|
||||
// Since this field has custom UI for invalid values, we also want to make
|
||||
// sure it knows it is now valid.
|
||||
this.isValueInvalid_ = false;
|
||||
};
|
||||
|
||||
// Notifies that the field that the new value was invalid. Called by
|
||||
// setValue function. Can either be triggered by the class validator, or the
|
||||
// local validator.
|
||||
CustomFields.FieldTurtle.prototype.doValueInvalid_ = function(invalidValue) {
|
||||
// By default this function is no-op, meaning if the new value is invalid
|
||||
// the field simply won't be updated. This field has custom UI for invalid
|
||||
// values, so we override this function.
|
||||
|
||||
// We want the value to be displayed like normal.
|
||||
// But we want to flag it as invalid, so the render_ function knows to
|
||||
// make the borderRect_ red.
|
||||
this.displayValue_ = invalidValue;
|
||||
this.isDirty_ = true;
|
||||
this.isValueInvalid_ = true;
|
||||
};
|
||||
|
||||
// Updates the field's on-block display based on the current display value.
|
||||
CustomFields.FieldTurtle.prototype.render_ = function() {
|
||||
var value = this.displayValue_;
|
||||
|
||||
// Always do editor updates inside render. This makes sure the editor
|
||||
// always displays the correct value, even if a validator changes it.
|
||||
if (this.editor_) {
|
||||
this.renderEditor_();
|
||||
}
|
||||
|
||||
this.stovepipe_.style.display = 'none';
|
||||
this.crown_.style.display = 'none';
|
||||
this.mask_.style.display = 'none';
|
||||
this.propeller_.style.display = 'none';
|
||||
this.fedora_.style.display = 'none';
|
||||
switch(value.hat) {
|
||||
case 'Stovepipe':
|
||||
this.stovepipe_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,12)');
|
||||
this.textElement_.setAttribute(
|
||||
'transform', 'translate(' + this.TEXT_OFFSET_X + ',20)');
|
||||
break;
|
||||
case 'Crown':
|
||||
this.crown_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,9)');
|
||||
this.textElement_.setAttribute(
|
||||
'transform', 'translate(' + this.TEXT_OFFSET_X + ',16)');
|
||||
break;
|
||||
case 'Mask':
|
||||
this.mask_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,6)');
|
||||
this.textElement_.setAttribute('transform',
|
||||
'translate(' + this.TEXT_OFFSET_X + ',12)');
|
||||
break;
|
||||
case 'Propeller':
|
||||
this.propeller_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,6)');
|
||||
this.textElement_.setAttribute('transform',
|
||||
'translate(' + this.TEXT_OFFSET_X + ',12)');
|
||||
break;
|
||||
case 'Fedora':
|
||||
this.fedora_.style.display = '';
|
||||
this.turtleGroup_.setAttribute('transform', 'translate(0,6)');
|
||||
this.textElement_.setAttribute('transform',
|
||||
'translate(' + this.TEXT_OFFSET_X + ',12)');
|
||||
break;
|
||||
}
|
||||
|
||||
switch(value.pattern) {
|
||||
case 'Dots':
|
||||
this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
|
||||
break;
|
||||
case 'Stripes':
|
||||
this.shellPattern_.setAttribute('fill', 'url(#stripes)');
|
||||
break;
|
||||
case 'Hexagons':
|
||||
this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
|
||||
break;
|
||||
}
|
||||
|
||||
// Always modify the textContent_ rather than the textElement_. This
|
||||
// allows fields to append DOM to the textElement (e.g. the angle field).
|
||||
this.textContent_.nodeValue = value.turtleName;
|
||||
|
||||
if (this.isValueInvalid_) {
|
||||
this.borderRect_.style.fill = '#f99';
|
||||
this.borderRect_.style.fillOpacity = 1;
|
||||
} else {
|
||||
this.borderRect_.style.fill = '#fff';
|
||||
this.borderRect_.style.fillOpacity = 0.6;
|
||||
}
|
||||
|
||||
this.updateSize_();
|
||||
};
|
||||
|
||||
CustomFields.FieldTurtle.prototype.renderEditor_ = function() {
|
||||
var value = this.displayValue_;
|
||||
|
||||
// .textElement is a property assigned to the element.
|
||||
// It allows the text to be edited without destroying the warning icon.
|
||||
this.editor_.patternText.textElement.nodeValue = value.pattern;
|
||||
this.editor_.hatText.textElement.nodeValue = value.hat;
|
||||
this.editor_.turtleNameText.textElement.nodeValue = value.turtleName;
|
||||
|
||||
this.editor_.patternText.warningIcon.style.display =
|
||||
this.cachedValidatedValue_.pattern ? 'none' : '';
|
||||
this.editor_.hatText.warningIcon.style.display =
|
||||
this.cachedValidatedValue_.hat ? 'none' : '';
|
||||
this.editor_.turtleNameText.warningIcon.style.display =
|
||||
this.cachedValidatedValue_.turtleName ? 'none' : '';
|
||||
};
|
||||
|
||||
// Used to update the size of the field. This function's logic could be simply
|
||||
// included inside render_ (it is not called anywhere else), but it is
|
||||
// usually separated to keep code more organized.
|
||||
CustomFields.FieldTurtle.prototype.updateSize_ = function() {
|
||||
var bbox = this.movableGroup_.getBBox();
|
||||
var width = bbox.width;
|
||||
var height = bbox.height;
|
||||
if (this.borderRect_) {
|
||||
width += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
|
||||
height += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
|
||||
this.borderRect_.setAttribute('width', width);
|
||||
this.borderRect_.setAttribute('height', height);
|
||||
}
|
||||
// Note how both the width and the height can be dynamic.
|
||||
this.size_.width = width;
|
||||
this.size_.height = height;
|
||||
};
|
||||
|
||||
// Called when the field is clicked. It is usually used to show an editor,
|
||||
// but it can also be used for other things e.g. the checkbox field uses
|
||||
// this function to check/uncheck itself.
|
||||
CustomFields.FieldTurtle.prototype.showEditor_ = function() {
|
||||
this.editor_ = this.dropdownCreate_();
|
||||
this.renderEditor_();
|
||||
Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);
|
||||
|
||||
// These allow us to have the editor match the block's colour.
|
||||
var fillColour = this.sourceBlock_.getColour();
|
||||
Blockly.DropDownDiv.setColour(fillColour,
|
||||
this.sourceBlock_.style.colourTertiary);
|
||||
|
||||
// Always pass the dropdown div a dispose function so that you can clean
|
||||
// up event listeners when the editor closes.
|
||||
Blockly.DropDownDiv.showPositionedByField(
|
||||
this, this.dropdownDispose_.bind(this));
|
||||
};
|
||||
|
||||
// Creates the UI of the editor, and adds event listeners to it.
|
||||
CustomFields.FieldTurtle.prototype.dropdownCreate_ = function() {
|
||||
var createRow = function(table) {
|
||||
var row = table.appendChild(document.createElement('tr'));
|
||||
row.className = 'row';
|
||||
return row;
|
||||
};
|
||||
var createLeftArrow = function(row) {
|
||||
var cell = document.createElement('div');
|
||||
cell.className = 'arrow';
|
||||
var leftArrow = document.createElement('button');
|
||||
leftArrow.setAttribute('type', 'button');
|
||||
leftArrow.textContent = '<';
|
||||
cell.appendChild(leftArrow);
|
||||
row.appendChild(cell);
|
||||
return cell;
|
||||
};
|
||||
var createTextNode = function(row, text) {
|
||||
var cell = document.createElement('div');
|
||||
cell.className = 'text';
|
||||
var text = document.createTextNode(text);
|
||||
cell.appendChild(text);
|
||||
cell.textElement = text;
|
||||
var warning = document.createElement('img');
|
||||
warning.setAttribute('src', 'media/warning.svg');
|
||||
warning.setAttribute('height', '16px');
|
||||
warning.setAttribute('width', '16px');
|
||||
warning.style.marginLeft = '4px';
|
||||
cell.appendChild(warning);
|
||||
cell.warningIcon = warning;
|
||||
row.appendChild(cell);
|
||||
return cell;
|
||||
};
|
||||
var createRightArrow = function(row) {
|
||||
var cell = document.createElement('div');
|
||||
cell.className = 'arrow';
|
||||
var rightArrow = document.createElement('button');
|
||||
rightArrow.setAttribute('type', 'button');
|
||||
rightArrow.textContent = '>';
|
||||
cell.appendChild(rightArrow);
|
||||
row.appendChild(cell);
|
||||
return cell;
|
||||
};
|
||||
var createArrowListener = function(variable, array, direction) {
|
||||
return function() {
|
||||
var currentIndex = array.indexOf(this.displayValue_[variable]);
|
||||
currentIndex += direction;
|
||||
if (currentIndex <= -1) {
|
||||
currentIndex = array.length - 1;
|
||||
} else if (currentIndex >= array.length) {
|
||||
currentIndex = 0;
|
||||
}
|
||||
var value = {};
|
||||
value[variable] = array[currentIndex];
|
||||
this.setValue(value);
|
||||
};
|
||||
};
|
||||
|
||||
var widget = document.createElement('div');
|
||||
widget.className = 'customFieldsTurtleWidget blocklyNonSelectable';
|
||||
|
||||
var table = document.createElement('div');
|
||||
table.className = 'table';
|
||||
widget.appendChild(table);
|
||||
|
||||
var row = createRow(table);
|
||||
var leftArrow = createLeftArrow(row);
|
||||
widget.patternText = createTextNode(row, this.displayValue_.pattern);
|
||||
var rightArrow = createRightArrow(row);
|
||||
this.editorListeners_.push(Blockly.browserEvents.bind(
|
||||
leftArrow, 'mouseup', this,
|
||||
createArrowListener('pattern', CustomFields.FieldTurtle.PATTERNS, -1)));
|
||||
this.editorListeners_.push(Blockly.browserEvents.bind(
|
||||
rightArrow, 'mouseup', this,
|
||||
createArrowListener('pattern', CustomFields.FieldTurtle.PATTERNS, 1)));
|
||||
|
||||
row = createRow(table);
|
||||
leftArrow = createLeftArrow(row);
|
||||
widget.hatText = createTextNode(row, this.displayValue_.hat);
|
||||
rightArrow = createRightArrow(row);
|
||||
this.editorListeners_.push(Blockly.browserEvents.bind(
|
||||
leftArrow, 'mouseup', this,
|
||||
createArrowListener('hat', CustomFields.FieldTurtle.HATS, -1)));
|
||||
this.editorListeners_.push(Blockly.browserEvents.bind(
|
||||
rightArrow, 'mouseup', this,
|
||||
createArrowListener('hat', CustomFields.FieldTurtle.HATS, 1)));
|
||||
|
||||
row = createRow(table);
|
||||
leftArrow = createLeftArrow(row);
|
||||
widget.turtleNameText = createTextNode(row, this.displayValue_.turtleName);
|
||||
rightArrow = createRightArrow(row);
|
||||
this.editorListeners_.push(Blockly.browserEvents.bind(
|
||||
leftArrow, 'mouseup', this,
|
||||
createArrowListener('turtleName', CustomFields.FieldTurtle.NAMES, -1)));
|
||||
this.editorListeners_.push(Blockly.browserEvents.bind(
|
||||
rightArrow, 'mouseup', this,
|
||||
createArrowListener('turtleName', CustomFields.FieldTurtle.NAMES, 1)));
|
||||
|
||||
var randomizeButton = document.createElement('button');
|
||||
randomizeButton.className = 'randomize';
|
||||
randomizeButton.setAttribute('type', 'button');
|
||||
randomizeButton.textContent = 'randomize turtle';
|
||||
this.editorListeners_.push(
|
||||
Blockly.browserEvents.bind(randomizeButton, 'mouseup', this, function() {
|
||||
var value = {};
|
||||
value.pattern = CustomFields.FieldTurtle.PATTERNS[Math.floor(
|
||||
Math.random() * CustomFields.FieldTurtle.PATTERNS.length)];
|
||||
|
||||
value.hat = CustomFields.FieldTurtle.HATS[Math.floor(
|
||||
Math.random() * CustomFields.FieldTurtle.HATS.length)];
|
||||
|
||||
value.turtleName = CustomFields.FieldTurtle.NAMES[Math.floor(
|
||||
Math.random() * CustomFields.FieldTurtle.NAMES.length)];
|
||||
|
||||
this.setValue(value);
|
||||
}));
|
||||
widget.appendChild(randomizeButton);
|
||||
|
||||
return widget;
|
||||
};
|
||||
|
||||
// Cleans up any event listeners that were attached to the now hidden editor.
|
||||
CustomFields.FieldTurtle.prototype.dropdownDispose_ = function() {
|
||||
for (var i = this.editorListeners_.length, listener;
|
||||
listener = this.editorListeners_[i]; i--) {
|
||||
Blockly.browserEvents.unbind(listener);
|
||||
this.editorListeners_.pop();
|
||||
}
|
||||
};
|
||||
|
||||
// Updates the field's colour based on the colour of the block. Called by
|
||||
// block.applyColour.
|
||||
CustomFields.FieldTurtle.prototype.applyColour = function() {
|
||||
if (!this.sourceBlock_) {
|
||||
return;
|
||||
}
|
||||
// The getColourX functions are the best way to access the colours of a block.
|
||||
var isShadow = this.sourceBlock_.isShadow();
|
||||
var fillColour = isShadow ?
|
||||
this.sourceBlock_.getColourShadow() : this.sourceBlock_.getColour();
|
||||
// This is technically a package function, meaning it could change.
|
||||
var borderColour = isShadow ? fillColour :
|
||||
this.sourceBlock_.style.colourTertiary;
|
||||
|
||||
if (this.turtleGroup_) {
|
||||
var child = this.turtleGroup_.firstChild;
|
||||
while(child) {
|
||||
// If it is a text node, continue.
|
||||
if (child.nodeType === 3) {
|
||||
child = child.nextSibling;
|
||||
continue;
|
||||
}
|
||||
// Or if it is a non-turtle node, continue.
|
||||
var className = child.getAttribute('class');
|
||||
if (!className || className.indexOf('turtleBody') === -1) {
|
||||
child = child.nextSibling;
|
||||
continue;
|
||||
}
|
||||
|
||||
child.style.fill = fillColour;
|
||||
child.style.stroke = borderColour;
|
||||
child = child.nextSibling;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Saves the field's value to an XML node. Allows for custom serialization.
|
||||
CustomFields.FieldTurtle.prototype.toXml = function(fieldElement) {
|
||||
// The default implementation of this function creates a node that looks
|
||||
// like this: (where value is returned by getValue())
|
||||
// <field name="FIELDNAME">value</field>
|
||||
// But this doesn't work for our field because it stores an /object/.
|
||||
|
||||
fieldElement.setAttribute('pattern', this.value_.pattern);
|
||||
fieldElement.setAttribute('hat', this.value_.hat);
|
||||
// The textContent usually contains whatever is closest to the field's
|
||||
// 'value'. The textContent doesn't need to contain anything, but saving
|
||||
// something to it does aid in readability.
|
||||
fieldElement.textContent = this.value_.turtleName;
|
||||
|
||||
// Always return the element!
|
||||
return fieldElement;
|
||||
};
|
||||
|
||||
// Sets the field's value based on an XML node. Allows for custom
|
||||
// de-serialization.
|
||||
CustomFields.FieldTurtle.prototype.fromXml = function(fieldElement) {
|
||||
// Because we had to do custom serialization for this field, we also need
|
||||
// to do custom de-serialization.
|
||||
|
||||
var value = {};
|
||||
value.pattern = fieldElement.getAttribute('pattern');
|
||||
value.hat = fieldElement.getAttribute('hat');
|
||||
value.turtleName = fieldElement.textContent;
|
||||
// The end goal is to call this.setValue()
|
||||
this.setValue(value);
|
||||
};
|
||||
|
||||
// Blockly needs to know the JSON name of this field. Usually this is
|
||||
// registered at the bottom of the field class.
|
||||
Blockly.fieldRegistry.register('field_turtle', CustomFields.FieldTurtle);
|
||||
|
||||
// Called by initView to create all of the SVGs. This is just used to keep
|
||||
// the code more organized.
|
||||
CustomFields.FieldTurtle.prototype.createView_ = function() {
|
||||
this.movableGroup_ = Blockly.utils.dom.createSvgElement('g',
|
||||
{
|
||||
'transform': 'translate(0,5)'
|
||||
}, this.fieldGroup_);
|
||||
var scaleGroup = Blockly.utils.dom.createSvgElement('g',
|
||||
{
|
||||
'transform': 'scale(1.5)'
|
||||
}, this.movableGroup_);
|
||||
this.turtleGroup_ = Blockly.utils.dom.createSvgElement('g',
|
||||
{
|
||||
// Makes the smaller turtle graphic align with the hats.
|
||||
'class': 'turtleBody'
|
||||
}, scaleGroup);
|
||||
var tail = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'd': 'M7,27.5H0.188c3.959-2,6.547-2.708,8.776-5.237',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
var legLeft = Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'x': 8.812,
|
||||
'y': 12.506,
|
||||
'width': 4,
|
||||
'height': 10
|
||||
}, this.turtleGroup_);
|
||||
var legRight = Blockly.utils.dom.createSvgElement('rect',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'x': 28.812,
|
||||
'y': 12.506,
|
||||
'width': 4,
|
||||
'height': 10
|
||||
}, this.turtleGroup_);
|
||||
var head = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'd': 'M47.991,17.884c0,1.92-2.144,3.477-4.788,3.477a6.262,6.262,0,0,1-2.212-.392c-0.2-.077-1.995,2.343-4.866,3.112a17.019,17.019,0,0,1-6.01.588c-4.413-.053-2.5-3.412-2.745-3.819-0.147-.242,2.232.144,6.126-0.376a7.392,7.392,0,0,0,4.919-2.588c0-1.92,2.144-3.477,4.788-3.477S47.991,15.964,47.991,17.884Z',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
var smile = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'd': 'M42.223,18.668a3.614,3.614,0,0,0,2.728,2.38',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
var sclera = Blockly.utils.dom.createSvgElement('ellipse',
|
||||
{
|
||||
'cx': 43.435,
|
||||
'cy': 2.61,
|
||||
'rx': 2.247,
|
||||
'ry': 2.61,
|
||||
'fill': '#fff'
|
||||
}, this.turtleGroup_);
|
||||
var pupil = Blockly.utils.dom.createSvgElement('ellipse',
|
||||
{
|
||||
'cx': 44.166,
|
||||
'cy': 3.403,
|
||||
'rx': 1.318,
|
||||
'ry': 1.62
|
||||
}, this.turtleGroup_);
|
||||
var shell = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'class': 'turtleBody',
|
||||
'd': 'M33.4,27.5H7.193c0-6,5.866-13.021,13.1-13.021S33.4,21.5,33.4,27.5Z',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
this.shellPattern_ = Blockly.utils.dom.createSvgElement('path',
|
||||
{
|
||||
'd': 'M33.4,27.5H7.193c0-6,5.866-13.021,13.1-13.021S33.4,21.5,33.4,27.5Z',
|
||||
'transform': 'translate(0.312 -12.994)'
|
||||
}, this.turtleGroup_);
|
||||
|
||||
this.stovepipe_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '18'
|
||||
}, scaleGroup);
|
||||
this.stovepipe_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/stovepipe.svg');
|
||||
this.crown_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '15'
|
||||
}, scaleGroup);
|
||||
this.crown_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/crown.svg');
|
||||
this.mask_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '24'
|
||||
}, scaleGroup);
|
||||
this.mask_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/mask.svg');
|
||||
this.propeller_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '11'
|
||||
}, scaleGroup);
|
||||
this.propeller_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/propeller.svg');
|
||||
this.fedora_ = Blockly.utils.dom.createSvgElement('image',
|
||||
{
|
||||
'width': '50',
|
||||
'height': '12'
|
||||
}, scaleGroup);
|
||||
this.fedora_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href',
|
||||
'media/fedora.svg');
|
||||
|
||||
// Even if we're not going to display it right away, we want to create all
|
||||
// of our DOM elements inside this function.
|
||||
this.crown_.style.display = 'none';
|
||||
this.mask_.style.display = 'none';
|
||||
this.propeller_.style.display = 'none';
|
||||
this.fedora_.style.display = 'none';
|
||||
|
||||
this.movableGroup_.appendChild(this.textElement_);
|
||||
this.textElement_.setAttribute(
|
||||
'transform', 'translate(' + this.TEXT_OFFSET_X + ',20)');
|
||||
|
||||
this.defs_ = Blockly.utils.dom.createSvgElement('defs', {}, this.fieldGroup_);
|
||||
this.polkadotPattern_ = Blockly.utils.dom.createSvgElement('pattern',
|
||||
{
|
||||
'id': 'polkadots',
|
||||
'patternUnits': 'userSpaceOnUse',
|
||||
'width': 10,
|
||||
'height': 10
|
||||
}, this.defs_);
|
||||
this.polkadotGroup_ = Blockly.utils.dom.createSvgElement(
|
||||
'g', {}, this.polkadotPattern_);
|
||||
Blockly.utils.dom.createSvgElement('circle',
|
||||
{
|
||||
'cx': 2.5,
|
||||
'cy': 2.5,
|
||||
'r': 2.5,
|
||||
'fill': '#000',
|
||||
'fill-opacity': .3
|
||||
}, this.polkadotGroup_);
|
||||
Blockly.utils.dom.createSvgElement('circle',
|
||||
{
|
||||
'cx': 7.5,
|
||||
'cy': 7.5,
|
||||
'r': 2.5,
|
||||
'fill': '#000',
|
||||
'fill-opacity': .3
|
||||
}, this.polkadotGroup_);
|
||||
|
||||
this.hexagonPattern_ = Blockly.utils.dom.createSvgElement('pattern',
|
||||
{
|
||||
'id': 'hexagons',
|
||||
'patternUnits': 'userSpaceOnUse',
|
||||
'width': 10,
|
||||
'height': 8.68,
|
||||
'patternTransform': 'translate(2) rotate(45)'
|
||||
}, this.defs_);
|
||||
Blockly.utils.dom.createSvgElement('polygon',
|
||||
{
|
||||
'id': 'hex',
|
||||
'points': '4.96,4.4 7.46,5.84 7.46,8.74 4.96,10.18 2.46,8.74 2.46,5.84',
|
||||
'stroke': '#000',
|
||||
'stroke-opacity': .3,
|
||||
'fill-opacity': 0
|
||||
}, this.hexagonPattern_);
|
||||
var use = Blockly.utils.dom.createSvgElement('use',
|
||||
{
|
||||
'x': 5,
|
||||
}, this.hexagonPattern_);
|
||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
|
||||
use = Blockly.utils.dom.createSvgElement('use',
|
||||
{
|
||||
'x': -5,
|
||||
}, this.hexagonPattern_);
|
||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
|
||||
use = Blockly.utils.dom.createSvgElement('use',
|
||||
{
|
||||
'x': 2.5,
|
||||
'y': -4.34
|
||||
}, this.hexagonPattern_);
|
||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
|
||||
use = Blockly.utils.dom.createSvgElement('use',
|
||||
{
|
||||
'x': -2.5,
|
||||
'y': -4.34
|
||||
}, this.hexagonPattern_);
|
||||
use.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', '#hex');
|
||||
|
||||
this.stripesPattern_ = Blockly.utils.dom.createSvgElement('pattern',
|
||||
{
|
||||
'id': 'stripes',
|
||||
'patternUnits': 'userSpaceOnUse',
|
||||
'width': 5,
|
||||
'height': 10,
|
||||
'patternTransform': 'rotate(45)'
|
||||
}, this.defs_);
|
||||
Blockly.utils.dom.createSvgElement('line',
|
||||
{
|
||||
'x1': 0,
|
||||
'y1': 0,
|
||||
'x2': 0,
|
||||
'y2': 10,
|
||||
'stroke-width': 4,
|
||||
'stroke': '#000',
|
||||
'stroke-opacity': .3
|
||||
}, this.stripesPattern_);
|
||||
};
|
||||
|
Before Width: | Height: | Size: 8.5 KiB |
@@ -1,173 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Blockly Demo: Custom Turtle Field</title>
|
||||
<script src="../../../blockly_uncompressed.js"></script>
|
||||
<script src="blocks.js"></script>
|
||||
<script src="field_turtle.js"></script>
|
||||
<script src="../../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 0 10%;
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
td {
|
||||
padding: 1ex;
|
||||
}
|
||||
img {
|
||||
border: none;
|
||||
}
|
||||
</style>
|
||||
<link rel="stylesheet" type="text/css" href="turtle.css">
|
||||
</head>
|
||||
<body onload="start()">
|
||||
<h1>
|
||||
<a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../../index.html">Demos</a> >
|
||||
<a href="../index.html">Custom Fields</a> > Turtle Field</h1>
|
||||
|
||||
|
||||
<p>This is a demo of creating custom block fields. In this case the field
|
||||
is used to define a turtle.
|
||||
</p>
|
||||
|
||||
<p>All of the custom field implementation is in
|
||||
demos/custom-fields/turtle/field_turtle.js, including comments on each required
|
||||
function.
|
||||
</p>
|
||||
|
||||
<p>Click on the blocks' comment icons to learn what they are demonstrating.
|
||||
Use the buttons below to see how the fields react to changes.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="button" value="set random style" onclick="setRandomStyle()">
|
||||
<input type="button" value="toggle shadow" onclick="toggleShadow()">
|
||||
<input type="button" value="toggle enabled" onclick="toggleEnabled()">
|
||||
<input type="button" value="toggle editable" onclick="toggleEditable()">
|
||||
<input type="button" value="toggle collapsed" onclick="toggleCollapsed()">
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<input type="button" value="Export to XML" onclick="toXml()">
|
||||
<input type="button" value="Import from XML" onclick="fromXml()">
|
||||
</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<textarea id="importExport"
|
||||
style="width: 200px; height: 480px;"></textarea>
|
||||
</td>
|
||||
<td>
|
||||
<div id="blocklyDiv" style="width: 600px; height: 480px;"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<script>
|
||||
function toXml() {
|
||||
var output = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.workspaceToDom(workspace);
|
||||
output.value = Blockly.Xml.domToPrettyText(xml);
|
||||
output.focus();
|
||||
output.select();
|
||||
}
|
||||
|
||||
function fromXml() {
|
||||
var input = document.getElementById('importExport');
|
||||
var xml = Blockly.Xml.textToDom(input.value);
|
||||
Blockly.Xml.domToWorkspace(xml, workspace);
|
||||
}
|
||||
|
||||
function setRandomStyle() {
|
||||
var blocks = workspace.getAllBlocks(false);
|
||||
var styles =
|
||||
Object.keys(workspace.getRenderer().getConstants().blockStyles);
|
||||
styles.splice(styles.indexOf(blocks[0].getStyleName()), 1);
|
||||
var style = styles[Math.floor(Math.random() * styles.length)];
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setStyle(style);
|
||||
}
|
||||
}
|
||||
|
||||
function toggleShadow() {
|
||||
var blocks = workspace.getAllBlocks(false);
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setShadow(!block.isShadow());
|
||||
}
|
||||
}
|
||||
|
||||
function toggleEnabled() {
|
||||
var blocks = workspace.getAllBlocks(false);
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setEnabled(!block.isEnabled());
|
||||
}
|
||||
}
|
||||
|
||||
function toggleEditable() {
|
||||
workspace.hideChaff();
|
||||
var blocks = workspace.getAllBlocks(false);
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setEditable(!block.isEditable());
|
||||
}
|
||||
}
|
||||
|
||||
function toggleCollapsed() {
|
||||
workspace.hideChaff();
|
||||
var blocks = workspace.getAllBlocks(false);
|
||||
for(var i = 0, block; block = blocks[i]; i++) {
|
||||
block.setCollapsed(!block.isCollapsed());
|
||||
}
|
||||
}
|
||||
|
||||
function appendDom() {
|
||||
var blocks = document.getElementById('workspace-blocks');
|
||||
if (blocks.firstElementChild) {
|
||||
Blockly.Xml.appendDomToWorkspace(blocks, workspace);
|
||||
}
|
||||
}
|
||||
|
||||
function start() {
|
||||
workspace = Blockly.inject('blocklyDiv', options);
|
||||
appendDom();
|
||||
workspace.scrollCenter();
|
||||
}
|
||||
|
||||
var options = {
|
||||
media: '../../../media/',
|
||||
grid: {
|
||||
spacing: 25,
|
||||
length: 3,
|
||||
colour: '#ccc'
|
||||
},
|
||||
move: {
|
||||
scrollbars: true,
|
||||
drag: true,
|
||||
wheel: true,
|
||||
},
|
||||
zoom: {
|
||||
controls: true,
|
||||
startScale: 1.0,
|
||||
maxScale: 4,
|
||||
minScale: 0.25,
|
||||
scaleSpeed: 1.1
|
||||
}
|
||||
/*toolbox: document.getElementById('toolbox')*/
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="workspace-blocks" style="display: none">
|
||||
<block type="turtle_basic"></block>
|
||||
<block type="turtle_nullifier" y="120"></block>
|
||||
<block type="turtle_changer" y="230"></block>
|
||||
</xml>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 15"><defs><style>.a{fill:#fff200;}.b{fill:#f3bd1f;}</style></defs><title>crown</title><polygon class="a" points="38.778 13.941 46.824 9.457 44.8 1.575 43.401 7.589 38.826 4.23 39.147 9.918 33.292 8.096 38.778 13.941"/><circle class="a" cx="33.228" cy="7.972" r="1.194" transform="translate(0.253 16.931) rotate(-28.691)"/><circle class="a" cx="38.785" cy="4.3" r="1.194" transform="translate(2.698 19.148) rotate(-28.691)"/><circle class="a" cx="44.662" cy="1.663" r="1.194" transform="translate(4.685 21.646) rotate(-28.691)"/><polygon class="b" points="37.846 12.954 46.508 8.199 46.154 6.836 36.888 11.921 37.846 12.954"/></svg>
|
||||
|
Before Width: | Height: | Size: 687 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 12"><defs><style>.a{fill:#8b5e3c;}.b{fill:#3c2415;}.c{fill:none;stroke:#754c29;stroke-linecap:round;stroke-miterlimit:10;}</style></defs><title>fedora</title><polygon class="a" points="38.548 9.742 38.264 4.844 41.403 5.223 42.508 1.407 47.846 5.918 46.334 7.664 38.548 9.742"/><path class="b" d="M41.133,8.362l1.1,0.236c1.9-1.066,3.844-2.051,4.741-3.392L45.78,4.193A22.044,22.044,0,0,1,41.133,8.362Z"/><path class="c" d="M37.233,11.3A12.1,12.1,0,0,1,40.4,9.09c1.7-.743,3.625-0.71,5.325-1.3a5.62,5.62,0,0,0,2.678-2.5"/></svg>
|
||||
|
Before Width: | Height: | Size: 581 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 14"><defs><style>.a{fill:#00aeef;}</style></defs><title>mask</title><path class="a" d="M43.5,0.94C42.329,1.2,39.032,3.75,39,4.531a21.249,21.249,0,0,0,3.5,1.811,10.847,10.847,0,0,0,4.065,0c0.751-.189,2.812-1.03,2.729-1.893-0.116-1.2-1.7-2.561-2.478-3.007A4.983,4.983,0,0,0,43.5.94Zm0.64,5.29a2.409,2.409,0,0,1-2.251-2.564,2.272,2.272,0,1,1,4.493,0A2.4,2.4,0,0,1,44.139,6.23Z"/><polygon class="a" points="38.996 4.531 42.106 9.154 40.018 13.776 38.996 4.531"/><polygon class="a" points="38.996 4.531 37.456 9.544 33.559 11.437 38.996 4.531"/></svg>
|
||||
|
Before Width: | Height: | Size: 602 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 11"><defs><style>.a{fill:#00aeef;}.b{fill:#ed1c24;}.c{fill:#fff200;}</style></defs><title>propeller</title><path class="a" d="M39.974,3.854c-2.022.99-2.948,3.89-1.509,6.828L40.716,9.56A10.329,10.329,0,0,1,39.974,3.854Z"/><path class="b" d="M39.974,3.854c1.975-.967,4.846-0.012,6.319,3L44.277,7.873A8.089,8.089,0,0,0,39.974,3.854Z"/><path class="c" d="M39.974,3.854a8.088,8.088,0,0,1,4.3,4.02l-3.55,1.71A10.011,10.011,0,0,1,39.974,3.854Z"/><path class="c" d="M35.795,4.493a3.81,3.81,0,0,1,1.016-.871A3.548,3.548,0,0,1,38.032,3.1a4.2,4.2,0,0,0,1.483-.434,9.15,9.15,0,0,0,1.245-.92A4.585,4.585,0,0,1,41.926,1.1a3.941,3.941,0,0,1,1.3-.3L43.364,1.1a3.941,3.941,0,0,1-1.031.84,4.585,4.585,0,0,1-1.219.53,9.149,9.149,0,0,0-1.49.419,4.194,4.194,0,0,0-1.252.905,3.549,3.549,0,0,1-1.164.642A3.81,3.81,0,0,1,35.9,4.7Z"/></svg>
|
||||
|
Before Width: | Height: | Size: 871 B |
@@ -1 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 50 18"><defs><style>.a,.c{fill:#231f20;stroke:#231f20;}.a,.b,.c{stroke-linejoin:round;}.b{fill:#fff;stroke:#fff;}.c{stroke-linecap:round;}</style></defs><title>stovepipe</title><rect class="a" x="33.995" y="2.114" width="8.322" height="11.675" transform="translate(0.82 19.139) rotate(-28.453)"/><rect class="b" x="36.206" y="10.835" width="8.322" height="2.394" transform="translate(-0.856 20.686) rotate(-28.453)"/><line class="c" x1="35.164" y1="16.971" x2="47.341" y2="10.373"/></svg>
|
||||
|
Before Width: | Height: | Size: 541 B |
@@ -1,5 +0,0 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16">
|
||||
<path style="fill: #00c; stroke: #fff; stroke-width: 1px;" d="M2,15Q-1,15 0.5,12L6.5,1.7Q8,-1 9.5,1.7L15.5,12Q17,15 14,15z"></path>
|
||||
<path style="fill: #fff;" d="m7,4.8v3.16l0.27,2.27h1.46l0.27,-2.27v-3.16z"></path>
|
||||
<rect style="fill: #fff;" x="7" y="11" height="2" width="2"></rect>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 363 B |
@@ -1,48 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2019 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
.customFieldsTurtleWidget {
|
||||
width: 150px;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget button {
|
||||
border-radius: 4px;
|
||||
border: none;
|
||||
background-color: #fff;
|
||||
opacity: .6;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .table {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .row {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .arrow {
|
||||
text-align: center;
|
||||
padding: 0;
|
||||
flex-grow: 0;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .text {
|
||||
height: 20px;
|
||||
color: #fff;
|
||||
flex-grow: 1;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.customFieldsTurtleWidget .randomize {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.blocklySvg .blocklyNonEditableText text,
|
||||
.blocklySvg .blocklyEditableText text {
|
||||
fill: #000;
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,49 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Fixed Blockly</title>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Fixed Blockly</h1>
|
||||
|
||||
<p>This is a simple demo of injecting Blockly into a fixed-sized 'div' element.</p>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/guides/configure-blockly/web/fixed-size">injecting fixed-sized Blockly</a>…</p>
|
||||
|
||||
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="controls_repeat_ext"></block>
|
||||
<block type="math_number">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="text"></block>
|
||||
<block type="text_print"></block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var demoWorkspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 2.6 KiB |
@@ -1,148 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Generating JavaScript</title>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Generating JavaScript</h1>
|
||||
|
||||
<p>This is a simple demo of generating code from blocks and running
|
||||
the code in a sandboxed JavaScript interpreter.</p>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/guides/configure/web/code-generators">Code Generators</a> and <a href="https://developers.google.com/blockly/guides/app-integration/running-javascript">Running JavaScript</a>.</p>
|
||||
|
||||
<p>
|
||||
<button onclick="showCode()">Show JavaScript</button>
|
||||
<button onclick="runCode()">Run JavaScript</button>
|
||||
</p>
|
||||
|
||||
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<category name="Logic" colour="%{BKY_LOGIC_HUE}">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
</category>
|
||||
<category name="Loops" colour="%{BKY_LOOPS_HUE}">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="controls_whileUntil"></block>
|
||||
</category>
|
||||
<category name="Math" colour="%{BKY_MATH_HUE}">
|
||||
<block type="math_number">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="math_single"></block>
|
||||
</category>
|
||||
<category name="Text" colour="%{BKY_TEXTS_HUE}">
|
||||
<block type="text"></block>
|
||||
<block type="text_length"></block>
|
||||
<block type="text_print"></block>
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="startBlocks" style="display: none">
|
||||
<block type="controls_if" inline="false" x="20" y="20">
|
||||
<mutation else="1"></mutation>
|
||||
<value name="IF0">
|
||||
<block type="logic_compare" inline="true">
|
||||
<field name="OP">EQ</field>
|
||||
<value name="A">
|
||||
<block type="math_arithmetic" inline="true">
|
||||
<field name="OP">MULTIPLY</field>
|
||||
<value name="A">
|
||||
<block type="math_number">
|
||||
<field name="NUM">6</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number">
|
||||
<field name="NUM">7</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number">
|
||||
<field name="NUM">42</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO0">
|
||||
<block type="text_print" inline="false">
|
||||
<value name="TEXT">
|
||||
<block type="text">
|
||||
<field name="TEXT">Don't panic</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</statement>
|
||||
<statement name="ELSE">
|
||||
<block type="text_print" inline="false">
|
||||
<value name="TEXT">
|
||||
<block type="text">
|
||||
<field name="TEXT">Panic</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var demoWorkspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
|
||||
demoWorkspace);
|
||||
|
||||
function showCode() {
|
||||
// Generate JavaScript code and display it.
|
||||
Blockly.JavaScript.INFINITE_LOOP_TRAP = null;
|
||||
var code = Blockly.JavaScript.workspaceToCode(demoWorkspace);
|
||||
alert(code);
|
||||
}
|
||||
|
||||
function runCode() {
|
||||
// Generate JavaScript code and run it.
|
||||
window.LoopTrap = 1000;
|
||||
Blockly.JavaScript.INFINITE_LOOP_TRAP =
|
||||
'if (--window.LoopTrap === 0) throw "Infinite loop.";\n';
|
||||
var code = Blockly.JavaScript.workspaceToCode(demoWorkspace);
|
||||
Blockly.JavaScript.INFINITE_LOOP_TRAP = null;
|
||||
try {
|
||||
eval(code);
|
||||
} catch (e) {
|
||||
alert(e);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 1.0 KiB |
@@ -1,364 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Graph</title>
|
||||
<script src="https://www.google.com/jsapi"></script>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
#funcText {
|
||||
margin-top: 1em;
|
||||
margin-left: 1.5em;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
#funcText>img {
|
||||
height: 3px;
|
||||
width: 15px;
|
||||
vertical-align: middle;
|
||||
margin-right: .5em;
|
||||
}
|
||||
#y1 {
|
||||
background-color: #36c;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Graph</h1>
|
||||
|
||||
<p>This is a demo of giving instant feedback as blocks are changed.</p>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/guides/configure/web/code-generators#generating_code">Realtime generation</a>…</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<div id="visualization" style="width: 400px"></div>
|
||||
</td>
|
||||
<td>
|
||||
<div id="blocklyDiv" style="height: 400px"></div>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<div id="funcText">
|
||||
<img id="y1" src="../../media/1x1.gif">
|
||||
...
|
||||
</div>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<category name="Math" colour="%{BKY_MATH_HUE}">
|
||||
<block type="math_number">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
<block type="math_arithmetic">
|
||||
<value name="A">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="B">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_single">
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">9</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_trig">
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">45</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_constant"></block>
|
||||
<block type="math_number_property">
|
||||
<value name="NUMBER_TO_CHECK">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">0</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_round">
|
||||
<value name="NUM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">3.1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_modulo">
|
||||
<value name="DIVIDEND">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">64</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="DIVISOR">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_constrain">
|
||||
<value name="VALUE">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">50</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="LOW">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="HIGH">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">100</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_random_int">
|
||||
<value name="FROM">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="TO">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">100</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
<block type="math_random_float"></block>
|
||||
<block type="math_atan2">
|
||||
<value name="X">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="Y">
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Variables" colour="%{BKY_VARIABLES_HUE}">
|
||||
<block type="graph_get_x"></block>
|
||||
</category>
|
||||
<category name="Logic" colour="%{BKY_LOGIC_HUE}">
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
<block type="logic_ternary"></block>
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="startBlocks" style="display: none">
|
||||
<block type="graph_set_y" deletable="false" x="100" y="100">
|
||||
<value name="VALUE">
|
||||
<block type="math_arithmetic">
|
||||
<field name="OP">POWER</field>
|
||||
<value name="A">
|
||||
<block type="graph_get_x"></block>
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number">
|
||||
<field name="NUM">2</field>
|
||||
</block>
|
||||
<shadow type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</shadow>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
// Load the Google Chart Tools Visualization API and the chart package.
|
||||
if (typeof google === 'object') {
|
||||
google.load('visualization', '1', {packages: ['corechart']});
|
||||
} else {
|
||||
alert('Unable to load Google\'s chart API.\n' +
|
||||
'Are you connected to the Internet?');
|
||||
}
|
||||
|
||||
// Define the custom blocks and their JS generators.
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "graph_get_x",
|
||||
"message0": "x",
|
||||
"output": "Number",
|
||||
"colour": Blockly.Msg['VARIABLES_HUE'],
|
||||
"tooltip": Blockly.Msg['VARIABLES_GET_TOOLTIP'],
|
||||
"helpUrl": Blockly.Msg['VARIABLES_GET_HELPURL']
|
||||
}]);
|
||||
|
||||
Blockly.JavaScript['graph_get_x'] = function(block) {
|
||||
// x variable getter.
|
||||
return ['x', Blockly.JavaScript.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "graph_set_y",
|
||||
"message0": "y = %1",
|
||||
"args0": [
|
||||
{
|
||||
"type": "input_value",
|
||||
"name": "VALUE",
|
||||
"check": "Number"
|
||||
}
|
||||
],
|
||||
"colour": Blockly.Msg['VARIABLES_HUE'],
|
||||
"tooltip": Blockly.Msg['VARIABLES_SET_TOOLTIP'],
|
||||
"helpUrl": Blockly.Msg['VARIABLES_SET_HELPURL']
|
||||
}]);
|
||||
|
||||
Blockly.JavaScript['graph_set_y'] = function(block) {
|
||||
// y variable setter.
|
||||
var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE',
|
||||
Blockly.JavaScript.ORDER_ASSIGNMENT) || '';
|
||||
return 'y = ' + argument0 + ';';
|
||||
};
|
||||
|
||||
/**
|
||||
* Create a namespace for the application.
|
||||
*/
|
||||
var Graph = {};
|
||||
|
||||
/**
|
||||
* Main Blockly workspace.
|
||||
* @type {Blockly.WorkspaceSvg}
|
||||
*/
|
||||
Graph.workspace = null;
|
||||
|
||||
/**
|
||||
* Cached copy of the function string.
|
||||
* @type {?string}
|
||||
* @private
|
||||
*/
|
||||
Graph.oldFormula_ = null;
|
||||
|
||||
/**
|
||||
* Drawing options for the Chart API.
|
||||
* @type {!Object}
|
||||
* @private
|
||||
*/
|
||||
Graph.options_ = {
|
||||
//curveType: 'function',
|
||||
width: 400, height: 400,
|
||||
chartArea: {left: '10%', width: '85%', height: '85%'}
|
||||
};
|
||||
|
||||
/**
|
||||
* Visualize the graph of y = f(x) using Google Chart Tools.
|
||||
* For more documentation on Google Chart Tools, see this linechart example:
|
||||
* https://developers.google.com/chart/interactive/docs/gallery/linechart
|
||||
*/
|
||||
Graph.drawVisualization = function() {
|
||||
var formula = Blockly.JavaScript.workspaceToCode(Graph.workspace);
|
||||
if (formula === Graph.oldFormula_) {
|
||||
// No change in the formula, don't recompute.
|
||||
return;
|
||||
}
|
||||
Graph.oldFormula_ = formula;
|
||||
|
||||
// Create and populate the data table.
|
||||
var data = google.visualization.arrayToDataTable(Graph.plot(formula));
|
||||
// Create and draw the visualization, passing in the data and options.
|
||||
new google.visualization.LineChart(document.getElementById('visualization')).
|
||||
draw(data, Graph.options_);
|
||||
|
||||
// Create the "y = ..." label. Find the relevant part of the code.
|
||||
formula = formula.substring(formula.indexOf('y = '));
|
||||
formula = formula.substring(0, formula.indexOf(';'));
|
||||
var funcText = document.getElementById('funcText');
|
||||
funcText.replaceChild(document.createTextNode(formula), funcText.lastChild);
|
||||
};
|
||||
|
||||
/**
|
||||
* Plot points on the function y = f(x).
|
||||
* @param {string} code JavaScript code.
|
||||
* @return {!Array<!Array>} 2D Array of points on the graph.
|
||||
*/
|
||||
Graph.plot = function(code) {
|
||||
// Initialize a table with two column headings.
|
||||
var table = [];
|
||||
var y;
|
||||
// TODO: Improve range and scale of graph.
|
||||
for (var x = -10; x <= 10; x = Math.round((x + 0.1) * 10) / 10) {
|
||||
try {
|
||||
eval(code);
|
||||
} catch (e) {
|
||||
y = NaN;
|
||||
}
|
||||
if (!isNaN(y)) {
|
||||
// Prevent y from being displayed inconsistently, some in decimals, some
|
||||
// in scientific notation, often when y has accumulated rounding errors.
|
||||
y = Math.round(y * Math.pow(10, 14)) / Math.pow(10, 14);
|
||||
table.push([x, y]);
|
||||
}
|
||||
}
|
||||
// Add column heading to table.
|
||||
if (table.length) {
|
||||
table.unshift(['x', 'y']);
|
||||
} else {
|
||||
// If the table is empty, add a [0, 0] row to prevent graph error.
|
||||
table.unshift(['x', 'y'], [0, 0]);
|
||||
}
|
||||
return table;
|
||||
};
|
||||
|
||||
/**
|
||||
* Force Blockly to resize into the available width.
|
||||
*/
|
||||
Graph.resize = function() {
|
||||
var width = Math.max(window.innerWidth - 440, 250);
|
||||
document.getElementById('blocklyDiv').style.width = width + 'px';
|
||||
Blockly.svgResize(Graph.workspace);
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize Blockly and the graph. Called on page load.
|
||||
*/
|
||||
Graph.init = function() {
|
||||
Graph.workspace = Blockly.inject('blocklyDiv',
|
||||
{collapse: false,
|
||||
disable: false,
|
||||
media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
|
||||
Graph.workspace);
|
||||
Graph.workspace.clearUndo();
|
||||
|
||||
// When Blockly changes, update the graph.
|
||||
Graph.workspace.addChangeListener(Graph.drawVisualization);
|
||||
Graph.workspace.addChangeListener(Blockly.Events.disableOrphans);
|
||||
Graph.resize();
|
||||
};
|
||||
|
||||
window.addEventListener('load', Graph.init);
|
||||
window.addEventListener('resize', Graph.resize);
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -28,18 +28,6 @@
|
||||
their own applications.</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>
|
||||
<a href="fixed/index.html">
|
||||
<img src="fixed/icon.png" height=80 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="fixed/index.html">Fixed Blockly</a></div>
|
||||
<div>Inject Blockly into a page as a fixed element.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="resizable/index.html">
|
||||
@@ -64,30 +52,6 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="maxBlocks/index.html">
|
||||
<img src="maxBlocks/icon.png" height=80 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="maxBlocks/index.html">Maximum Block Limit</a></div>
|
||||
<div>Limit the total number of blocks allowed (for academic exercises).</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="generator/index.html">
|
||||
<img src="generator/icon.png" height=80 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="generator/index.html">Generate JavaScript</a></div>
|
||||
<div>Turn blocks into code and execute it.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="headless/index.html">
|
||||
@@ -100,31 +64,6 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="interpreter/index.html">
|
||||
<img src="interpreter/icon.png" height=80 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div style="font-weight: bold">JS Interpreter</div>
|
||||
<div>Demo #1: <a href="interpreter/step-execution.html">Step by step execution in JavaScript.</a></div>
|
||||
<div>Demo #2: <a href="interpreter/async-execution.html">Asynchronous execution in JavaScript.</a></div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="graph/index.html">
|
||||
<img src="graph/icon.png" height=80 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="graph/index.html">Graph</a></div>
|
||||
<div>Instant updates when blocks are changed.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="rtl/index.html">
|
||||
@@ -137,30 +76,6 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="custom-dialogs/index.html">
|
||||
<img src="custom-dialogs/icon.png" height=80 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="custom-dialogs/index.html">Custom Dialogs</a></div>
|
||||
<div>Override Blockly browser dialogs with custom implementations.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="custom-fields/index.html">
|
||||
<img src="custom-fields/turtle/icon.png" height=80 width=100>
|
||||
</a>
|
||||
</td>
|
||||
<td>
|
||||
<div><a href="custom-fields/index.html">Custom Fields</a></div>
|
||||
<div>Implement a custom field.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<td>
|
||||
<a href="storage/index.html">
|
||||
|
||||
@@ -1,267 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Asynchronous Execution with JS Interpreter</title>
|
||||
<script src="acorn_interpreter.js"></script>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<script src="wait_block.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Asynchronous Execution with JS Interpreter</h1>
|
||||
|
||||
<p>This is a demo of executing code asynchronously (e.g., waiting for delays or user input) using the JavaScript interpreter.</p>
|
||||
|
||||
<p>→ <a href="https://developers.google.com/blockly/guides/configure-blockly/web/running-javascript#js_interpreter">More info on running code with JS Interpreter</a></p>
|
||||
|
||||
<p>
|
||||
<button onclick="runCode()" id="runButton">Run JavaScript</button>
|
||||
</p>
|
||||
|
||||
<div style="width: 100%">
|
||||
<div id="blocklyDiv"
|
||||
style="display: inline-block; height: 480px; width: 58%"></div>
|
||||
<textarea id="output" disabled="disabled"
|
||||
style="display: inline-block; height: 480px; width: 38%;">
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<category name="Logic" colour="%{BKY_LOGIC_HUE}">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
</category>
|
||||
<category name="Loops" colour="%{BKY_LOOPS_HUE}">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="controls_whileUntil"></block>
|
||||
</category>
|
||||
<category name="Math" colour="%{BKY_MATH_HUE}">
|
||||
<block type="math_number">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="math_single"></block>
|
||||
</category>
|
||||
<category name="Text" colour="%{BKY_TEXTS_HUE}">
|
||||
<block type="text"></block>
|
||||
<block type="text_length"></block>
|
||||
<block type="text_print"></block>
|
||||
<block type="text_prompt_ext">
|
||||
<value name="TEXT">
|
||||
<block type="text"></block>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<sep></sep>
|
||||
<category name="Variables" custom="VARIABLE" colour="%{BKY_VARIABLES_HUE}">
|
||||
</category>
|
||||
<category name="Functions" custom="PROCEDURE" colour="%{BKY_PROCEDURES_HUE}">
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="startBlocks" style="display: none">
|
||||
<block type="variables_set" id="set_n_initial" inline="true" x="20" y="20">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_repeat_ext" id="repeat" inline="true">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">4</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="wait_seconds" id="wait">
|
||||
<field name="SECONDS">1.0</field>
|
||||
<next>
|
||||
<block type="variables_set" id="set_n_update" inline="true">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_arithmetic" inline="true">
|
||||
<field name="OP">MULTIPLY</field>
|
||||
<value name="A">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number">
|
||||
<field name="NUM">2</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="print" inline="false">
|
||||
<value name="TEXT">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var demoWorkspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
|
||||
demoWorkspace);
|
||||
|
||||
// Exit is used to signal the end of a script.
|
||||
Blockly.JavaScript.addReservedWords('exit');
|
||||
|
||||
var outputArea = document.getElementById('output');
|
||||
var runButton = document.getElementById('runButton');
|
||||
var myInterpreter = null;
|
||||
var runner;
|
||||
|
||||
function initApi(interpreter, globalObject) {
|
||||
// Add an API function for the alert() block, generated for "text_print" blocks.
|
||||
var wrapper = function(text) {
|
||||
text = text ? text.toString() : '';
|
||||
outputArea.value = outputArea.value + '\n' + text;
|
||||
};
|
||||
interpreter.setProperty(globalObject, 'alert',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
|
||||
// Add an API function for the prompt() block.
|
||||
var wrapper = function(text) {
|
||||
text = text ? text.toString() : '';
|
||||
return interpreter.createPrimitive(prompt(text));
|
||||
};
|
||||
interpreter.setProperty(globalObject, 'prompt',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
|
||||
// Add an API for the wait block. See wait_block.js
|
||||
initInterpreterWaitForSeconds(interpreter, globalObject);
|
||||
|
||||
// Add an API function for highlighting blocks.
|
||||
var wrapper = function(id) {
|
||||
id = id ? id.toString() : '';
|
||||
return interpreter.createPrimitive(highlightBlock(id));
|
||||
};
|
||||
interpreter.setProperty(globalObject, 'highlightBlock',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
}
|
||||
|
||||
var highlightPause = false;
|
||||
var latestCode = '';
|
||||
|
||||
function highlightBlock(id) {
|
||||
demoWorkspace.highlightBlock(id);
|
||||
highlightPause = true;
|
||||
}
|
||||
|
||||
function resetStepUi(clearOutput) {
|
||||
demoWorkspace.highlightBlock(null);
|
||||
highlightPause = false;
|
||||
runButton.disabled = '';
|
||||
|
||||
if (clearOutput) {
|
||||
outputArea.value = 'Program output:\n=================';
|
||||
}
|
||||
}
|
||||
|
||||
function generateCodeAndLoadIntoInterpreter() {
|
||||
// Generate JavaScript code and parse it.
|
||||
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
|
||||
Blockly.JavaScript.addReservedWords('highlightBlock');
|
||||
latestCode = Blockly.JavaScript.workspaceToCode(demoWorkspace);
|
||||
|
||||
resetStepUi(true);
|
||||
}
|
||||
|
||||
function resetInterpreter() {
|
||||
myInterpreter = null;
|
||||
if (runner) {
|
||||
clearTimeout(runner);
|
||||
runner = null;
|
||||
}
|
||||
}
|
||||
|
||||
function runCode() {
|
||||
if (!myInterpreter) {
|
||||
// First statement of this code.
|
||||
// Clear the program output.
|
||||
resetStepUi(true);
|
||||
runButton.disabled = 'disabled';
|
||||
|
||||
// And then show generated code in an alert.
|
||||
// In a timeout to allow the outputArea.value to reset first.
|
||||
setTimeout(function() {
|
||||
alert('Ready to execute the following code\n' +
|
||||
'===================================\n' +
|
||||
latestCode);
|
||||
|
||||
// Begin execution
|
||||
highlightPause = false;
|
||||
myInterpreter = new Interpreter(latestCode, initApi);
|
||||
runner = function() {
|
||||
if (myInterpreter) {
|
||||
var hasMore = myInterpreter.run();
|
||||
if (hasMore) {
|
||||
// Execution is currently blocked by some async call.
|
||||
// Try again later.
|
||||
setTimeout(runner, 10);
|
||||
} else {
|
||||
// Program is complete.
|
||||
outputArea.value += '\n\n<< Program complete >>';
|
||||
resetInterpreter();
|
||||
resetStepUi(false);
|
||||
}
|
||||
}
|
||||
};
|
||||
runner();
|
||||
}, 1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Load the interpreter now, and upon future changes.
|
||||
generateCodeAndLoadIntoInterpreter();
|
||||
demoWorkspace.addChangeListener(function(event) {
|
||||
if (!(event instanceof Blockly.Events.Ui)) {
|
||||
// Something changed. Parser needs to be reloaded.
|
||||
resetInterpreter();
|
||||
generateCodeAndLoadIntoInterpreter();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,11 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<title>Redirecting...</title>
|
||||
<meta http-equiv="refresh" content="0;URL='step-execution.html'"/>
|
||||
</head>
|
||||
<body>
|
||||
Redirecting to <a href="step-execution.html">step execution JS-Interpreter demo</a>.
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,254 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Step Execution with JS Interpreter</title>
|
||||
<script src="acorn_interpreter.js"></script>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../javascript_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Step Execution with JS Interpreter</h1>
|
||||
|
||||
<p>This is a demo of executing code step-by-step with a sandboxed JavaScript interpreter.</p>
|
||||
|
||||
<p>The generator's <code>Blockly.JavaScript.STATEMENT_PREFIX</code> is assigned <code>'highlightBlock(%1);\n'</code>,
|
||||
where <code>%1</code> is the block id. The call to <code>highlightBlock()</code> will highlight the identified block
|
||||
and set the variable <code>highlightPause</code> to <code>true</code>.</p>
|
||||
|
||||
<p>"Parse JavaScript" will generate the code and load it into the interpreter. Then, each press of the
|
||||
"Step JavaScript" button will run the interpreter one step until the <code>highlightPause</code> is true.
|
||||
That is, until <code>highlightBlock()</code> has highlighted the block that will be executed on the next step.</p>
|
||||
|
||||
<p>→ <a href="https://developers.google.com/blockly/guides/configure-blockly/web/running-javascript#js_interpreter">More info on running code with JS Interpreter</a></p>
|
||||
|
||||
<p>
|
||||
<button onclick="stepCode()" id="stepButton">Step JavaScript</button>
|
||||
</p>
|
||||
|
||||
<div style="width: 100%">
|
||||
<div id="blocklyDiv"
|
||||
style="display: inline-block; height: 480px; width: 58%"></div>
|
||||
<textarea id="output" disabled="disabled"
|
||||
style="display: inline-block; height: 480px; width: 38%;">
|
||||
</textarea>
|
||||
</div>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<category name="Logic" colour="%{BKY_LOGIC_HUE}">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
</category>
|
||||
<category name="Loops" colour="%{BKY_LOOPS_HUE}">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="controls_whileUntil"></block>
|
||||
</category>
|
||||
<category name="Math" colour="%{BKY_MATH_HUE}">
|
||||
<block type="math_number">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="math_single"></block>
|
||||
</category>
|
||||
<category name="Text" colour="%{BKY_TEXTS_HUE}">
|
||||
<block type="text"></block>
|
||||
<block type="text_length"></block>
|
||||
<block type="text_print"></block>
|
||||
<block type="text_prompt_ext">
|
||||
<value name="TEXT">
|
||||
<block type="text"></block>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<sep></sep>
|
||||
<category name="Variables" custom="VARIABLE" colour="%{BKY_VARIABLES_HUE}">
|
||||
</category>
|
||||
<category name="Functions" custom="PROCEDURE" colour="%{BKY_PROCEDURES_HUE}">
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="startBlocks" style="display: none">
|
||||
<block type="variables_set" id="set_n_initial" inline="true" x="20" y="20">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="controls_repeat_ext" id="repeat" inline="true">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">4</field>
|
||||
</block>
|
||||
</value>
|
||||
<statement name="DO">
|
||||
<block type="variables_set" id="set_n_update" inline="true">
|
||||
<field name="VAR">n</field>
|
||||
<value name="VALUE">
|
||||
<block type="math_arithmetic" inline="true">
|
||||
<field name="OP">MULTIPLY</field>
|
||||
<value name="A">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="B">
|
||||
<block type="math_number">
|
||||
<field name="NUM">2</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</value>
|
||||
<next>
|
||||
<block type="text_print" id="print">
|
||||
<value name="TEXT">
|
||||
<block type="variables_get">
|
||||
<field name="VAR">n</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
</next>
|
||||
</block>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var demoWorkspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
Blockly.Xml.domToWorkspace(document.getElementById('startBlocks'),
|
||||
demoWorkspace);
|
||||
|
||||
var outputArea = document.getElementById('output');
|
||||
var stepButton = document.getElementById('stepButton');
|
||||
var myInterpreter = null;
|
||||
|
||||
function initApi(interpreter, globalObject) {
|
||||
// Add an API function for the alert() block, generated for "text_print" blocks.
|
||||
interpreter.setProperty(globalObject, 'alert',
|
||||
interpreter.createNativeFunction(function(text) {
|
||||
text = arguments.length ? text : '';
|
||||
outputArea.value += '\n' + text;
|
||||
}));
|
||||
|
||||
// Add an API function for the prompt() block.
|
||||
var wrapper = function(text) {
|
||||
return interpreter.createPrimitive(prompt(text));
|
||||
};
|
||||
interpreter.setProperty(globalObject, 'prompt',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
|
||||
// Add an API function for highlighting blocks.
|
||||
var wrapper = function(id) {
|
||||
id = String(id || '');
|
||||
return interpreter.createPrimitive(highlightBlock(id));
|
||||
};
|
||||
interpreter.setProperty(globalObject, 'highlightBlock',
|
||||
interpreter.createNativeFunction(wrapper));
|
||||
}
|
||||
|
||||
var highlightPause = false;
|
||||
var latestCode = '';
|
||||
|
||||
function highlightBlock(id) {
|
||||
demoWorkspace.highlightBlock(id);
|
||||
highlightPause = true;
|
||||
}
|
||||
|
||||
function resetStepUi(clearOutput) {
|
||||
demoWorkspace.highlightBlock(null);
|
||||
highlightPause = false;
|
||||
|
||||
if (clearOutput) {
|
||||
outputArea.value = 'Program output:\n=================';
|
||||
}
|
||||
}
|
||||
|
||||
function generateCodeAndLoadIntoInterpreter() {
|
||||
// Generate JavaScript code and parse it.
|
||||
Blockly.JavaScript.STATEMENT_PREFIX = 'highlightBlock(%1);\n';
|
||||
Blockly.JavaScript.addReservedWords('highlightBlock');
|
||||
latestCode = Blockly.JavaScript.workspaceToCode(demoWorkspace);
|
||||
resetStepUi(true);
|
||||
}
|
||||
|
||||
function stepCode() {
|
||||
if (!myInterpreter) {
|
||||
// First statement of this code.
|
||||
// Clear the program output.
|
||||
resetStepUi(true);
|
||||
myInterpreter = new Interpreter(latestCode, initApi);
|
||||
|
||||
// And then show generated code in an alert.
|
||||
// In a timeout to allow the outputArea.value to reset first.
|
||||
setTimeout(function() {
|
||||
alert('Ready to execute the following code\n' +
|
||||
'===================================\n' + latestCode);
|
||||
highlightPause = true;
|
||||
stepCode();
|
||||
}, 1);
|
||||
return;
|
||||
}
|
||||
highlightPause = false;
|
||||
do {
|
||||
try {
|
||||
var hasMoreCode = myInterpreter.step();
|
||||
} finally {
|
||||
if (!hasMoreCode) {
|
||||
// Program complete, no more code to execute.
|
||||
outputArea.value += '\n\n<< Program complete >>';
|
||||
|
||||
myInterpreter = null;
|
||||
resetStepUi(false);
|
||||
|
||||
// Cool down, to discourage accidentally restarting the program.
|
||||
stepButton.disabled = 'disabled';
|
||||
setTimeout(function() {
|
||||
stepButton.disabled = '';
|
||||
}, 2000);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Keep executing until a highlight statement is reached,
|
||||
// or the code completes or errors.
|
||||
} while (hasMoreCode && !highlightPause);
|
||||
}
|
||||
|
||||
// Load the interpreter now, and upon future changes.
|
||||
generateCodeAndLoadIntoInterpreter();
|
||||
demoWorkspace.addChangeListener(function(event) {
|
||||
if (!(event instanceof Blockly.Events.Ui)) {
|
||||
// Something changed. Parser needs to be reloaded.
|
||||
generateCodeAndLoadIntoInterpreter();
|
||||
}
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,53 +0,0 @@
|
||||
/**
|
||||
* @license
|
||||
* Copyright 2017 Google LLC
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Example "wait" block that will pause the interpreter for a
|
||||
* number of seconds. Because wait is a blocking behavior, such blocks will
|
||||
* only work in interpreted environments.
|
||||
*
|
||||
* See https://neil.fraser.name/software/JS-Interpreter/docs.html
|
||||
*/
|
||||
Blockly.defineBlocksWithJsonArray([{
|
||||
"type": "wait_seconds",
|
||||
"message0": " wait %1 seconds",
|
||||
"args0": [{
|
||||
"type": "field_number",
|
||||
"name": "SECONDS",
|
||||
"min": 0,
|
||||
"max": 600,
|
||||
"value": 1
|
||||
}],
|
||||
"previousStatement": null,
|
||||
"nextStatement": null,
|
||||
"colour": "%{BKY_LOOPS_HUE}"
|
||||
}]);
|
||||
|
||||
/**
|
||||
* Generator for wait block creates call to new method
|
||||
* <code>waitForSeconds()</code>.
|
||||
*/
|
||||
Blockly.JavaScript['wait_seconds'] = function(block) {
|
||||
var seconds = Number(block.getFieldValue('SECONDS'));
|
||||
var code = 'waitForSeconds(' + seconds + ');\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
/**
|
||||
* Register the interpreter asynchronous function
|
||||
* <code>waitForSeconds()</code>.
|
||||
*/
|
||||
function initInterpreterWaitForSeconds(interpreter, globalObject) {
|
||||
// Ensure function name does not conflict with variable names.
|
||||
Blockly.JavaScript.addReservedWords('waitForSeconds');
|
||||
|
||||
var wrapper = interpreter.createAsyncFunction(
|
||||
function(timeInSeconds, callback) {
|
||||
// Delay the call to the callback.
|
||||
setTimeout(callback, timeInSeconds * 1000);
|
||||
});
|
||||
interpreter.setProperty(globalObject, 'waitForSeconds', wrapper);
|
||||
}
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
@@ -1,100 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>Blockly Demo: Maximum Block Limit</title>
|
||||
<script src="../../blockly_compressed.js"></script>
|
||||
<script src="../../blocks_compressed.js"></script>
|
||||
<script src="../../msg/js/en.js"></script>
|
||||
<style>
|
||||
body {
|
||||
background-color: #fff;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 140%;
|
||||
}
|
||||
#capacity {
|
||||
color: red;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1><a href="https://developers.google.com/blockly/">Blockly</a> >
|
||||
<a href="../index.html">Demos</a> > Maximum Block Limit</h1>
|
||||
|
||||
<p>This is a demo of Blockly which has been restricted to a maximum of
|
||||
five blocks.</p>
|
||||
|
||||
<p><b>You have <span id="capacity"></span> block(s) left.</b></p>
|
||||
|
||||
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
|
||||
|
||||
<xml xmlns="https://developers.google.com/blockly/xml" id="toolbox" style="display: none">
|
||||
<category name="Logic" colour="%{BKY_LOGIC_HUE}">
|
||||
<block type="controls_if"></block>
|
||||
<block type="logic_compare"></block>
|
||||
<block type="logic_operation"></block>
|
||||
<block type="logic_negate"></block>
|
||||
<block type="logic_boolean"></block>
|
||||
</category>
|
||||
<category name="Loops" colour="%{BKY_LOOPS_HUE}">
|
||||
<block type="controls_repeat_ext">
|
||||
<value name="TIMES">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
<block type="controls_whileUntil"></block>
|
||||
<block type="controls_for">
|
||||
<field name="VAR">i</field>
|
||||
<value name="FROM">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="TO">
|
||||
<block type="math_number">
|
||||
<field name="NUM">10</field>
|
||||
</block>
|
||||
</value>
|
||||
<value name="BY">
|
||||
<block type="math_number">
|
||||
<field name="NUM">1</field>
|
||||
</block>
|
||||
</value>
|
||||
</block>
|
||||
</category>
|
||||
<category name="Math" colour="%{BKY_MATH_HUE}">
|
||||
<block type="math_number">
|
||||
<field name="NUM">123</field>
|
||||
</block>
|
||||
<block type="math_arithmetic"></block>
|
||||
<block type="math_single"></block>
|
||||
</category>
|
||||
<category name="Text" colour="%{BKY_TEXTS_HUE}">
|
||||
<block type="text"></block>
|
||||
<block type="text_length"></block>
|
||||
<block type="text_print"></block>
|
||||
</category>
|
||||
</xml>
|
||||
|
||||
<script>
|
||||
var demoWorkspace = Blockly.inject('blocklyDiv',
|
||||
{media: '../../media/',
|
||||
maxBlocks: 5,
|
||||
toolbox: document.getElementById('toolbox')});
|
||||
|
||||
function onchange(event) {
|
||||
document.getElementById('capacity').textContent =
|
||||
demoWorkspace.remainingCapacity();
|
||||
}
|
||||
|
||||
demoWorkspace.addChangeListener(onchange);
|
||||
onchange();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||