mirror of
https://github.com/scratchfoundation/scratch-blocks.git
synced 2025-08-28 22:10:31 -04:00
Merge remote-tracking branch 'Google/develop' into feature/upstream-merge-march-11-2
# Conflicts: # blockly_compressed_vertical.js # blocks_compressed.js # build.py # core/connection.js # core/workspace.js # tests/playground.html
This commit is contained in:
commit
516cd05d82
53 changed files with 3446 additions and 608 deletions
|
@ -30,6 +30,7 @@ blockly/
|
|||
|- blocks_compressed.js
|
||||
|- dart_compressed.js
|
||||
|- javascript_compressed.js
|
||||
|- lua_compressed.js
|
||||
|- php_compressed.js
|
||||
`- python_compressed.js
|
||||
|
||||
|
|
|
@ -46,7 +46,8 @@ goog.addDependency("../../../" + dir + "/core/blocks.js", ['Blockly.Blocks'], []
|
|||
goog.addDependency("../../../" + dir + "/core/bubble.js", ['Blockly.Bubble'], ['Blockly.Workspace', 'goog.dom', 'goog.math', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/colours.js", ['Blockly.Colours'], []);
|
||||
goog.addDependency("../../../" + dir + "/core/comment.js", ['Blockly.Comment'], ['Blockly.Bubble', 'Blockly.Icon', 'goog.userAgent']);
|
||||
goog.addDependency("../../../" + dir + "/core/connection.js", ['Blockly.Connection', 'Blockly.ConnectionDB'], ['goog.dom']);
|
||||
goog.addDependency("../../../" + dir + "/core/connection.js", ['Blockly.Connection'], ['goog.asserts', 'goog.dom']);
|
||||
goog.addDependency("../../../" + dir + "/core/connection_db.js", ['Blockly.ConnectionDB'], ['Blockly.Connection']);
|
||||
goog.addDependency("../../../" + dir + "/core/contextmenu.js", ['Blockly.ContextMenu'], ['goog.dom', 'goog.events', 'goog.style', 'goog.ui.Menu', 'goog.ui.MenuItem']);
|
||||
goog.addDependency("../../../" + dir + "/core/css.js", ['Blockly.Css'], ['Blockly.Colours']);
|
||||
goog.addDependency("../../../" + dir + "/core/events.js", ['Blockly.Events'], []);
|
||||
|
|
|
@ -123,8 +123,17 @@ Blockly.Blocks['lists_create_with'] = {
|
|||
this.updateShape_();
|
||||
// Reconnect any child blocks.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
if (connections[i]) {
|
||||
this.getInput('ADD' + i).connection.connect(connections[i]);
|
||||
var connectionChild = connections[i];
|
||||
if (connectionChild) {
|
||||
var parent = connectionChild.targetBlock();
|
||||
var connectionParent = this.getInput('ADD' + i).connection;
|
||||
if (connectionParent.targetConnection != connectionChild &&
|
||||
(!parent || parent == this)) {
|
||||
if (connectionParent.targetConnection) {
|
||||
connectionParent.disconnect();
|
||||
}
|
||||
connectionParent.connect(connectionChild);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -150,28 +159,26 @@ Blockly.Blocks['lists_create_with'] = {
|
|||
* @this Blockly.Block
|
||||
*/
|
||||
updateShape_: function() {
|
||||
// Delete everything.
|
||||
if (this.getInput('EMPTY')) {
|
||||
if (this.itemCount_ && this.getInput('EMPTY')) {
|
||||
this.removeInput('EMPTY');
|
||||
} else {
|
||||
var i = 0;
|
||||
while (this.getInput('ADD' + i)) {
|
||||
this.removeInput('ADD' + i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
// Rebuild block.
|
||||
if (this.itemCount_ == 0) {
|
||||
} else if (!this.itemCount_ && !this.getInput('EMPTY')) {
|
||||
this.appendDummyInput('EMPTY')
|
||||
.appendField(Blockly.Msg.LISTS_CREATE_EMPTY_TITLE);
|
||||
} else {
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
}
|
||||
// Add new inputs.
|
||||
for (var i = 0; i < this.itemCount_; i++) {
|
||||
if (!this.getInput('ADD' + i)) {
|
||||
var input = this.appendValueInput('ADD' + i);
|
||||
if (i == 0) {
|
||||
input.appendField(Blockly.Msg.LISTS_CREATE_WITH_INPUT_WITH);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Remove deleted inputs.
|
||||
while (this.getInput('ADD' + i)) {
|
||||
this.removeInput('ADD' + i);
|
||||
i++;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -330,7 +330,7 @@ Blockly.Blocks['logic_compare'] = {
|
|||
for (var i = 0; i < this.prevBlocks_.length; i++) {
|
||||
var block = this.prevBlocks_[i];
|
||||
if (block === blockA || block === blockB) {
|
||||
block.setParent(null);
|
||||
block.unplug();
|
||||
block.bumpNeighbours_();
|
||||
}
|
||||
}
|
||||
|
@ -470,10 +470,10 @@ Blockly.Blocks['logic_ternary'] = {
|
|||
var block = (i == 1) ? blockA : blockB;
|
||||
if (block && !block.outputConnection.checkType_(parentConnection)) {
|
||||
if (parentConnection === this.prevParentConnection_) {
|
||||
this.setParent(null);
|
||||
this.unplug();
|
||||
parentConnection.sourceBlock_.bumpNeighbours_();
|
||||
} else {
|
||||
block.setParent(null);
|
||||
block.unplug();
|
||||
block.bumpNeighbours_();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -234,7 +234,7 @@ Blockly.Blocks['procedures_defnoreturn'] = {
|
|||
this.statementConnection_ = stackConnection.targetConnection;
|
||||
if (this.statementConnection_) {
|
||||
var stackBlock = stackConnection.targetBlock();
|
||||
stackBlock.setParent(null);
|
||||
stackBlock.unplug();
|
||||
stackBlock.bumpNeighbours_();
|
||||
}
|
||||
this.setStatements_(false);
|
||||
|
|
|
@ -605,11 +605,7 @@ Blockly.Blocks['text_prompt_ext'] = {
|
|||
* @this Blockly.Block
|
||||
*/
|
||||
updateType_: function(newOp) {
|
||||
if (newOp == 'NUMBER') {
|
||||
this.outputConnection.setCheck('Number');
|
||||
} else {
|
||||
this.outputConnection.setCheck('String');
|
||||
}
|
||||
this.outputConnection.setCheck(newOp == 'NUMBER' ? 'Number' : 'String');
|
||||
},
|
||||
/**
|
||||
* Create XML to represent the output type.
|
||||
|
|
1
build.py
1
build.py
|
@ -33,6 +33,7 @@
|
|||
# javascript_compressed.js: The compressed Javascript generator.
|
||||
# python_compressed.js: The compressed Python generator.
|
||||
# dart_compressed.js: The compressed Dart generator.
|
||||
# lua_compressed.js: The compressed Lua generator.
|
||||
# msg/js/<LANG>.js for every language <LANG> defined in msg/js/<LANG>.json.
|
||||
|
||||
import sys
|
||||
|
|
|
@ -241,27 +241,28 @@ Blockly.Block.prototype.dispose = function(healStack) {
|
|||
/**
|
||||
* Unplug this block from its superior block. If this block is a statement,
|
||||
* optionally reconnect the block underneath with the block on top.
|
||||
* @param {boolean} healStack Disconnect child statement and reconnect stack.
|
||||
* @param {boolean} opt_healStack Disconnect child statement and reconnect
|
||||
* stack. Defaults to false.
|
||||
*/
|
||||
Blockly.Block.prototype.unplug = function(healStack) {
|
||||
Blockly.Block.prototype.unplug = function(opt_healStack) {
|
||||
if (this.outputConnection) {
|
||||
if (this.outputConnection.targetConnection) {
|
||||
// Disconnect from any superior block.
|
||||
this.setParent(null);
|
||||
this.outputConnection.disconnect();
|
||||
}
|
||||
} else {
|
||||
} else if (this.previousConnection) {
|
||||
var previousTarget = null;
|
||||
if (this.previousConnection && this.previousConnection.targetConnection) {
|
||||
if (this.previousConnection.targetConnection) {
|
||||
// Remember the connection that any next statements need to connect to.
|
||||
previousTarget = this.previousConnection.targetConnection;
|
||||
// Detach this block from the parent's tree.
|
||||
this.setParent(null);
|
||||
this.previousConnection.disconnect();
|
||||
}
|
||||
var nextBlock = this.getNextBlock();
|
||||
if (healStack && nextBlock) {
|
||||
if (opt_healStack && nextBlock) {
|
||||
// Disconnect the next statement.
|
||||
var nextTarget = this.nextConnection.targetConnection;
|
||||
nextBlock.setParent(null);
|
||||
nextTarget.disconnect();
|
||||
if (previousTarget && previousTarget.checkType_(nextTarget)) {
|
||||
// Attach the next statement to the previous statement.
|
||||
previousTarget.connect(nextTarget);
|
||||
|
@ -424,9 +425,8 @@ Blockly.Block.prototype.getChildren = function() {
|
|||
* @param {Blockly.Block} newParent New parent block.
|
||||
*/
|
||||
Blockly.Block.prototype.setParent = function(newParent) {
|
||||
var event;
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
event = new Blockly.Events.Move(this);
|
||||
if (newParent == this.parentBlock_) {
|
||||
return;
|
||||
}
|
||||
if (this.parentBlock_) {
|
||||
// Remove this block from the old parent's child list.
|
||||
|
@ -439,13 +439,13 @@ Blockly.Block.prototype.setParent = function(newParent) {
|
|||
}
|
||||
|
||||
// Disconnect from superior blocks.
|
||||
this.parentBlock_ = null;
|
||||
if (this.previousConnection && this.previousConnection.targetConnection) {
|
||||
this.previousConnection.disconnect();
|
||||
throw 'Still connected to previous block.';
|
||||
}
|
||||
if (this.outputConnection && this.outputConnection.targetConnection) {
|
||||
this.outputConnection.disconnect();
|
||||
throw 'Still connected to parent block.';
|
||||
}
|
||||
this.parentBlock_ = null;
|
||||
// This block hasn't actually moved on-screen, so there's no need to update
|
||||
// its connection locations.
|
||||
} else {
|
||||
|
@ -460,10 +460,6 @@ Blockly.Block.prototype.setParent = function(newParent) {
|
|||
} else {
|
||||
this.workspace.addTopBlock(this);
|
||||
}
|
||||
if (event) {
|
||||
event.recordNew();
|
||||
Blockly.Events.fire(event);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -1303,7 +1299,7 @@ Blockly.Block.prototype.removeInput = function(name, opt_quiet) {
|
|||
if (input.name == name) {
|
||||
if (input.connection && input.connection.targetConnection) {
|
||||
// Disconnect any attached block.
|
||||
input.connection.targetBlock().setParent(null);
|
||||
input.connection.targetBlock().unplug();
|
||||
}
|
||||
input.dispose();
|
||||
this.inputList.splice(i, 1);
|
||||
|
@ -1361,7 +1357,7 @@ Blockly.Block.prototype.getCommentText = function() {
|
|||
Blockly.Block.prototype.setCommentText = function(text) {
|
||||
if (this.comment != text) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
this, 'comment', null, this.comment, text || ''));
|
||||
this, 'comment', null, this.comment, text || ''));
|
||||
this.comment = text;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -218,10 +218,18 @@ Blockly.BlockSvg.terminateDrag_ = function() {
|
|||
delete selected.draggedBubbles_;
|
||||
selected.setDragging_(false);
|
||||
selected.render();
|
||||
goog.Timer.callOnce(
|
||||
selected.snapToGrid, Blockly.BUMP_DELAY / 2, selected);
|
||||
goog.Timer.callOnce(
|
||||
selected.bumpNeighbours_, Blockly.BUMP_DELAY, selected);
|
||||
// Ensure that any stap and bump are part of this move's event group.
|
||||
var group = Blockly.Events.getGroup();
|
||||
setTimeout(function() {
|
||||
Blockly.Events.setGroup(group);
|
||||
selected.snapToGrid();
|
||||
Blockly.Events.setGroup(false);
|
||||
}, Blockly.BUMP_DELAY / 2);
|
||||
setTimeout(function() {
|
||||
Blockly.Events.setGroup(group);
|
||||
selected.bumpNeighbours_();
|
||||
Blockly.Events.setGroup(false);
|
||||
}, Blockly.BUMP_DELAY);
|
||||
// Fire an event to allow scrollbars to resize.
|
||||
Blockly.fireUiEvent(window, 'resize');
|
||||
}
|
||||
|
@ -235,6 +243,9 @@ Blockly.BlockSvg.terminateDrag_ = function() {
|
|||
* @param {Blockly.BlockSvg} newParent New parent block.
|
||||
*/
|
||||
Blockly.BlockSvg.prototype.setParent = function(newParent) {
|
||||
if (newParent == this.parentBlock_) {
|
||||
return;
|
||||
}
|
||||
var svgRoot = this.getSvgRoot();
|
||||
if (this.parentBlock_ && svgRoot) {
|
||||
// Move this block up the DOM. Keep track of x/y translations.
|
||||
|
@ -393,7 +404,9 @@ Blockly.BlockSvg.prototype.onMouseDown_ = function(e) {
|
|||
// dragged instead.
|
||||
return;
|
||||
} else {
|
||||
Blockly.Events.group = Blockly.genUid();
|
||||
if (!Blockly.Events.getGroup()) {
|
||||
Blockly.Events.setGroup(true);
|
||||
}
|
||||
// Left-click (or middle click)
|
||||
Blockly.removeAllRanges();
|
||||
Blockly.Css.setCursor(Blockly.Css.Cursor.CLOSED);
|
||||
|
@ -473,7 +486,9 @@ Blockly.BlockSvg.prototype.onMouseUp_ = function(e) {
|
|||
Blockly.highlightedConnection_ = null;
|
||||
}
|
||||
Blockly.Css.setCursor(Blockly.Css.Cursor.OPEN);
|
||||
Blockly.Events.group = '';
|
||||
if (!Blockly.WidgetDiv.isVisible()) {
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -647,7 +662,7 @@ Blockly.BlockSvg.prototype.onMouseMove_ = function(e) {
|
|||
group.skew_ = '';
|
||||
if (this.parentBlock_) {
|
||||
// Push this block to the very top of the stack.
|
||||
this.setParent(null);
|
||||
this.unplug();
|
||||
this.disconnectUiEffect();
|
||||
}
|
||||
this.setDragging_(true);
|
||||
|
|
|
@ -392,6 +392,9 @@ Blockly.onKeyDown_ = function(e) {
|
|||
if (Blockly.clipboardXml_) {
|
||||
Blockly.clipboardSource_.paste(Blockly.clipboardXml_);
|
||||
}
|
||||
} else if (e.keyCode == 90) {
|
||||
// 'z' for undo 'Z' is for redo.
|
||||
Blockly.mainWorkspace.undo(e.shiftKey);
|
||||
}
|
||||
}
|
||||
if (deleteBlock) {
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Connection');
|
||||
goog.provide('Blockly.ConnectionDB');
|
||||
|
||||
goog.require('goog.asserts');
|
||||
goog.require('goog.dom');
|
||||
|
||||
|
||||
|
@ -74,10 +74,129 @@ Blockly.Connection.NUMBER = 3;
|
|||
Blockly.Connection.CAN_CONNECT = 0;
|
||||
Blockly.Connection.REASON_SELF_CONNECTION = 1;
|
||||
Blockly.Connection.REASON_WRONG_TYPE = 2;
|
||||
Blockly.Connection.REASON_MUST_DISCONNECT = 3;
|
||||
Blockly.Connection.REASON_TARGET_NULL = 4;
|
||||
Blockly.Connection.REASON_CHECKS_FAILED = 5;
|
||||
Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 6;
|
||||
Blockly.Connection.REASON_TARGET_NULL = 3;
|
||||
Blockly.Connection.REASON_CHECKS_FAILED = 4;
|
||||
Blockly.Connection.REASON_DIFFERENT_WORKSPACES = 5;
|
||||
|
||||
/**
|
||||
* Connect two connections together.
|
||||
* @param {!Blockly.Connection} parentConnection Connection on superior block.
|
||||
* @param {!Blockly.Connection} childConnection Connection on inferior block.
|
||||
*/
|
||||
Blockly.Connection.connect_ = function(parentConnection, childConnection) {
|
||||
var parentBlock = parentConnection.sourceBlock_;
|
||||
var childBlock = childConnection.sourceBlock_;
|
||||
// Disconnect any existing parent on the child connection.
|
||||
if (childConnection.targetConnection) {
|
||||
childConnection.disconnect();
|
||||
}
|
||||
if (parentConnection.targetConnection) {
|
||||
// Other connection is already connected to something.
|
||||
// Disconnect it and reattach it or bump it as needed.
|
||||
var orphanBlock = parentConnection.targetBlock();
|
||||
var shadowDom = parentConnection.getShadowDom();
|
||||
// Temporarily set the shadow DOM to null so it does not respawn.
|
||||
parentConnection.setShadowDom(null);
|
||||
// Displaced shadow blocks dissolve rather than reattaching or bumping.
|
||||
if (orphanBlock.isShadow()) {
|
||||
// Save the shadow block so that field values are preserved.
|
||||
shadowDom = Blockly.Xml.blockToDom(orphanBlock);
|
||||
orphanBlock.dispose();
|
||||
orphanBlock = null;
|
||||
} else if (parentConnection.type == Blockly.INPUT_VALUE) {
|
||||
// Value connections.
|
||||
// If female block is already connected, disconnect and bump the male.
|
||||
if (!orphanBlock.outputConnection) {
|
||||
throw 'Orphan block does not have an output connection.';
|
||||
}
|
||||
// Attempt to reattach the orphan at the end of the newly inserted
|
||||
// block. Since this block may be a row, walk down to the end
|
||||
// or to the first (and only) shadow block.
|
||||
var connection = Blockly.Connection.lastConnectionInRow_(
|
||||
childBlock, orphanBlock);
|
||||
if (connection) {
|
||||
orphanBlock.outputConnection.connect(connection);
|
||||
orphanBlock = null;
|
||||
}
|
||||
} else if (parentConnection.type == Blockly.NEXT_STATEMENT) {
|
||||
// Statement connections.
|
||||
// Statement blocks may be inserted into the middle of a stack.
|
||||
// Split the stack.
|
||||
if (!orphanBlock.previousConnection) {
|
||||
throw 'Orphan block does not have a previous connection.';
|
||||
}
|
||||
// Attempt to reattach the orphan at the bottom of the newly inserted
|
||||
// block. Since this block may be a stack, walk down to the end.
|
||||
var newBlock = childBlock;
|
||||
while (newBlock.nextConnection) {
|
||||
if (newBlock.nextConnection.targetConnection) {
|
||||
newBlock = newBlock.getNextBlock();
|
||||
} else {
|
||||
if (orphanBlock.previousConnection.checkType_(
|
||||
newBlock.nextConnection)) {
|
||||
newBlock.nextConnection.connect(orphanBlock.previousConnection);
|
||||
orphanBlock = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (orphanBlock) {
|
||||
// Unable to reattach orphan.
|
||||
parentConnection.disconnect();
|
||||
if (Blockly.Events.recordUndo) {
|
||||
// Bump it off to the side after a moment.
|
||||
var group = Blockly.Events.getGroup();
|
||||
setTimeout(function() {
|
||||
// Verify orphan hasn't been deleted or reconnected (user on meth).
|
||||
if (orphanBlock.workspace && !orphanBlock.getParent()) {
|
||||
Blockly.Events.setGroup(group);
|
||||
if (orphanBlock.outputConnection) {
|
||||
orphanBlock.outputConnection.bumpAwayFrom_(parentConnection);
|
||||
} else if (orphanBlock.previousConnection) {
|
||||
orphanBlock.previousConnection.bumpAwayFrom_(parentConnection);
|
||||
}
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
}, Blockly.BUMP_DELAY);
|
||||
}
|
||||
}
|
||||
// Restore the shadow DOM.
|
||||
parentConnection.setShadowDom(shadowDom);
|
||||
}
|
||||
|
||||
var event;
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
event = new Blockly.Events.Move(childBlock);
|
||||
}
|
||||
// Establish the connections.
|
||||
Blockly.Connection.connectReciprocally_(parentConnection, childConnection);
|
||||
// Demote the inferior block so that one is a child of the superior one.
|
||||
childBlock.setParent(parentBlock);
|
||||
if (event) {
|
||||
event.recordNew();
|
||||
Blockly.Events.fire(event);
|
||||
}
|
||||
|
||||
if (parentBlock.rendered) {
|
||||
parentBlock.updateDisabled();
|
||||
}
|
||||
if (childBlock.rendered) {
|
||||
childBlock.updateDisabled();
|
||||
}
|
||||
if (parentBlock.rendered && childBlock.rendered) {
|
||||
if (parentConnection.type == Blockly.NEXT_STATEMENT ||
|
||||
parentConnection.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
// Child block may need to square off its corners if it is in a stack.
|
||||
// Rendering a child will render its parent.
|
||||
childBlock.render();
|
||||
} else {
|
||||
// Child block does not change shape. Rendering the parent node will
|
||||
// move its connected children into position.
|
||||
parentBlock.render();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Connection this connection connects to. Null if not connected.
|
||||
|
@ -171,6 +290,18 @@ Blockly.Connection.prototype.isSuperior = function() {
|
|||
this.type == Blockly.NEXT_STATEMENT;
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns the distance between this connection and another connection.
|
||||
* @param {!Blockly.Connection} otherConnection The other connection to measure
|
||||
* the distance to.
|
||||
* @return {number} The distance between connections.
|
||||
*/
|
||||
Blockly.Connection.prototype.distanceFrom = function(otherConnection) {
|
||||
var xDiff = this.x_ - otherConnection.x_;
|
||||
var yDiff = this.y_ - otherConnection.y_;
|
||||
return Math.sqrt(xDiff * xDiff + yDiff * yDiff);
|
||||
};
|
||||
|
||||
/**
|
||||
* Checks whether the current connection can connect with the target
|
||||
* connection.
|
||||
|
@ -186,8 +317,6 @@ Blockly.Connection.prototype.canConnectWithReason_ = function(target) {
|
|||
return Blockly.Connection.REASON_SELF_CONNECTION;
|
||||
} else if (target.type != Blockly.OPPOSITE_TYPE[this.type]) {
|
||||
return Blockly.Connection.REASON_WRONG_TYPE;
|
||||
} else if (this.targetConnection) {
|
||||
return Blockly.Connection.REASON_MUST_DISCONNECT;
|
||||
} else if (this.sourceBlock_ && target.sourceBlock_ &&
|
||||
this.sourceBlock_.workspace !== target.sourceBlock_.workspace) {
|
||||
return Blockly.Connection.REASON_DIFFERENT_WORKSPACES;
|
||||
|
@ -214,8 +343,6 @@ Blockly.Connection.prototype.checkConnection_ = function(target) {
|
|||
throw 'Blocks are on different workspaces.';
|
||||
case Blockly.Connection.REASON_WRONG_TYPE:
|
||||
throw 'Attempt to connect incompatible types.';
|
||||
case Blockly.Connection.REASON_MUST_DISCONNECT:
|
||||
throw 'Source connection already connected.';
|
||||
case Blockly.Connection.REASON_TARGET_NULL:
|
||||
throw 'Target connection is null.';
|
||||
case Blockly.Connection.REASON_CHECKS_FAILED:
|
||||
|
@ -225,117 +352,78 @@ Blockly.Connection.prototype.checkConnection_ = function(target) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Check if the two connections can be dragged to connect to each other.
|
||||
* @param {!Blockly.Connection} candidate A nearby connection to check.
|
||||
* @param {number} maxRadius The maximum radius allowed for connections.
|
||||
* @return {boolean} True if the connection is allowed, false otherwise.
|
||||
*/
|
||||
Blockly.Connection.prototype.isConnectionAllowed = function(candidate,
|
||||
maxRadius) {
|
||||
if (this.distanceFrom(candidate) > maxRadius) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Type checking.
|
||||
var canConnect = this.canConnectWithReason_(candidate);
|
||||
if (canConnect != Blockly.Connection.CAN_CONNECT &&
|
||||
canConnect != Blockly.Connection.REASON_MUST_DISCONNECT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't offer to connect an already connected left (male) value plug to
|
||||
// an available right (female) value plug. Don't offer to connect the
|
||||
// bottom of a statement block to one that's already connected.
|
||||
if (candidate.type == Blockly.OUTPUT_VALUE ||
|
||||
candidate.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
if (candidate.targetConnection || this.targetConnection) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// Offering to connect the left (male) of a value block to an already
|
||||
// connected value pair is ok, we'll splice it in.
|
||||
// However, don't offer to splice into an unmovable block.
|
||||
if (candidate.type == Blockly.INPUT_VALUE &&
|
||||
candidate.targetConnection &&
|
||||
!candidate.targetBlock().isMovable() &&
|
||||
!candidate.targetBlock().isShadow()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't let blocks try to connect to themselves or ones they nest.
|
||||
var targetSourceBlock = candidate.sourceBlock_;
|
||||
var sourceBlock = this.sourceBlock_;
|
||||
if (targetSourceBlock && sourceBlock) {
|
||||
do {
|
||||
if (sourceBlock == targetSourceBlock) {
|
||||
return false;
|
||||
}
|
||||
targetSourceBlock = targetSourceBlock.getParent();
|
||||
} while (targetSourceBlock);
|
||||
}
|
||||
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Connect this connection to another connection.
|
||||
* @param {!Blockly.Connection} otherConnection Connection to connect to.
|
||||
*/
|
||||
Blockly.Connection.prototype.connect = function(otherConnection) {
|
||||
this.checkConnection_(otherConnection);
|
||||
// If the previous statement failed it would have thrown an exception.
|
||||
|
||||
if (otherConnection.targetConnection) {
|
||||
// Other connection is already connected to something.
|
||||
// Disconnect it and reattach it or bump it as needed.
|
||||
var orphanBlock = otherConnection.targetBlock();
|
||||
orphanBlock.setParent(null);
|
||||
// Displaced shadow blocks dissolve rather than reattaching or bumping.
|
||||
if (orphanBlock.isShadow()) {
|
||||
// Save the shadow block so that field values are preserved.
|
||||
otherConnection.setShadowDom(Blockly.Xml.blockToDom(orphanBlock));
|
||||
orphanBlock.dispose();
|
||||
orphanBlock = null;
|
||||
} else if (this.type == Blockly.INPUT_VALUE ||
|
||||
this.type == Blockly.OUTPUT_VALUE) {
|
||||
// Value connections.
|
||||
// If female block is already connected, disconnect and bump the male.
|
||||
if (!orphanBlock.outputConnection) {
|
||||
throw 'Orphan block does not have an output connection.';
|
||||
}
|
||||
// Attempt to reattach the orphan at the end of the newly inserted
|
||||
// block. Since this block may be a row, walk down to the end
|
||||
// or to the first (and only) shadow block.
|
||||
var connection = Blockly.Connection.lastConnectionInRow_(
|
||||
this.sourceBlock_, orphanBlock);
|
||||
if (connection != null) {
|
||||
orphanBlock.outputConnection.connect(connection);
|
||||
orphanBlock = null;
|
||||
}
|
||||
} else if (this.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
// Statement connections.
|
||||
// Statement blocks may be inserted into the middle of a stack.
|
||||
// Split the stack.
|
||||
if (!orphanBlock.previousConnection) {
|
||||
throw 'Orphan block does not have a previous connection.';
|
||||
}
|
||||
// Attempt to reattach the orphan at the bottom of the newly inserted
|
||||
// block. Since this block may be a stack, walk down to the end.
|
||||
var newBlock = this.sourceBlock_;
|
||||
while (newBlock.nextConnection) {
|
||||
if (newBlock.nextConnection.targetConnection) {
|
||||
newBlock = newBlock.getNextBlock();
|
||||
} else {
|
||||
if (orphanBlock.previousConnection.checkType_(
|
||||
newBlock.nextConnection)) {
|
||||
newBlock.nextConnection.connect(orphanBlock.previousConnection);
|
||||
orphanBlock = null;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Type is Blockly.NEXT_STATEMENT.
|
||||
throw 'Can only do a mid-stack connection with the top of a block.';
|
||||
}
|
||||
if (orphanBlock) {
|
||||
// Unable to reattach orphan. Bump it off to the side after a moment.
|
||||
setTimeout(function() {
|
||||
// Verify orphan hasn't been deleted or reconnected (user on meth).
|
||||
if (orphanBlock.workspace && !orphanBlock.getParent()) {
|
||||
if (orphanBlock.outputConnection) {
|
||||
orphanBlock.outputConnection.bumpAwayFrom_(otherConnection);
|
||||
} else if (orphanBlock.previousConnection) {
|
||||
orphanBlock.previousConnection.bumpAwayFrom_(otherConnection);
|
||||
}
|
||||
}
|
||||
}, Blockly.BUMP_DELAY);
|
||||
}
|
||||
if (this.targetConnection == otherConnection) {
|
||||
// Already connected together. NOP.
|
||||
return;
|
||||
}
|
||||
|
||||
this.checkConnection_(otherConnection);
|
||||
// Determine which block is superior (higher in the source stack).
|
||||
var parentBlock, childBlock;
|
||||
if (this.isSuperior()) {
|
||||
// Superior block.
|
||||
parentBlock = this.sourceBlock_;
|
||||
childBlock = otherConnection.sourceBlock_;
|
||||
Blockly.Connection.connect_(this, otherConnection);
|
||||
} else {
|
||||
// Inferior block.
|
||||
parentBlock = otherConnection.sourceBlock_;
|
||||
childBlock = this.sourceBlock_;
|
||||
}
|
||||
|
||||
// Establish the connections.
|
||||
Blockly.Connection.connectReciprocally(this, otherConnection);
|
||||
|
||||
// Demote the inferior block so that one is a child of the superior one.
|
||||
childBlock.setParent(parentBlock);
|
||||
|
||||
if (parentBlock.rendered) {
|
||||
parentBlock.updateDisabled();
|
||||
}
|
||||
if (childBlock.rendered) {
|
||||
childBlock.updateDisabled();
|
||||
}
|
||||
if (parentBlock.rendered && childBlock.rendered) {
|
||||
if (this.type == Blockly.NEXT_STATEMENT ||
|
||||
this.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
// Child block may need to square off its corners if it is in a stack.
|
||||
// Rendering a child will render its parent.
|
||||
childBlock.render();
|
||||
} else {
|
||||
// Child block does not change shape. Rendering the parent node will
|
||||
// move its connected children into position.
|
||||
parentBlock.render();
|
||||
}
|
||||
Blockly.Connection.connect_(otherConnection, this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -343,11 +431,10 @@ Blockly.Connection.prototype.connect = function(otherConnection) {
|
|||
* Update two connections to target each other.
|
||||
* @param {Blockly.Connection} first The first connection to update.
|
||||
* @param {Blockly.Connection} second The second conneciton to update.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Connection.connectReciprocally = function(first, second) {
|
||||
if (!first || !second) {
|
||||
throw 'Cannot connect null connections.';
|
||||
}
|
||||
Blockly.Connection.connectReciprocally_ = function(first, second) {
|
||||
goog.asserts.assert(first && second, 'Cannot connect null connections.');
|
||||
first.targetConnection = second;
|
||||
second.targetConnection = first;
|
||||
};
|
||||
|
@ -407,15 +494,10 @@ Blockly.Connection.lastConnectionInRow_ = function(startBlock, orphanBlock) {
|
|||
*/
|
||||
Blockly.Connection.prototype.disconnect = function() {
|
||||
var otherConnection = this.targetConnection;
|
||||
if (!otherConnection) {
|
||||
throw 'Source connection not connected.';
|
||||
} else if (otherConnection.targetConnection != this) {
|
||||
throw 'Target connection not connected to source connection.';
|
||||
}
|
||||
otherConnection.targetConnection = null;
|
||||
this.targetConnection = null;
|
||||
goog.asserts.assert(otherConnection, 'Source connection not connected.');
|
||||
goog.asserts.assert(otherConnection.targetConnection == this,
|
||||
'Target connection not connected to source connection.');
|
||||
|
||||
// Rerender the parent so that it may reflow.
|
||||
var parentBlock, childBlock, parentConnection;
|
||||
if (this.isSuperior()) {
|
||||
// Superior block.
|
||||
|
@ -428,9 +510,22 @@ Blockly.Connection.prototype.disconnect = function() {
|
|||
childBlock = this.sourceBlock_;
|
||||
parentConnection = otherConnection;
|
||||
}
|
||||
|
||||
var event;
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
event = new Blockly.Events.Move(childBlock);
|
||||
}
|
||||
otherConnection.targetConnection = null;
|
||||
this.targetConnection = null;
|
||||
childBlock.setParent(null);
|
||||
if (event) {
|
||||
event.recordNew();
|
||||
Blockly.Events.fire(event);
|
||||
}
|
||||
|
||||
// Respawn the shadow block if there is one.
|
||||
var shadow = parentConnection.getShadowDom();
|
||||
if (parentBlock.workspace && !childBlock.isShadow() && shadow) {
|
||||
// Respawn the shadow block.
|
||||
if (parentBlock.workspace && shadow && Blockly.Events.recordUndo) {
|
||||
var blockShadow =
|
||||
Blockly.Xml.domToBlock(parentBlock.workspace, shadow);
|
||||
if (blockShadow.outputConnection) {
|
||||
|
@ -443,6 +538,8 @@ Blockly.Connection.prototype.disconnect = function() {
|
|||
blockShadow.initSvg();
|
||||
blockShadow.render(false);
|
||||
}
|
||||
|
||||
// Rerender the parent so that it may reflow.
|
||||
if (parentBlock.rendered) {
|
||||
parentBlock.render();
|
||||
}
|
||||
|
@ -521,7 +618,7 @@ Blockly.Connection.prototype.moveTo = function(x, y) {
|
|||
this.y_ = y;
|
||||
// Insert it into its new location in the database.
|
||||
if (!this.hidden_) {
|
||||
this.db_.addConnection_(this);
|
||||
this.db_.addConnection(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -593,108 +690,18 @@ Blockly.Connection.prototype.tighten_ = function() {
|
|||
* in the database and the current location (as a result of dragging).
|
||||
* @param {number} dy Vertical offset between this connection's location
|
||||
* in the database and the current location (as a result of dragging).
|
||||
* @return {!{connection: ?Blockly.Connection, radius: number}} Contains two properties: 'connection' which is either
|
||||
* another connection or null, and 'radius' which is the distance.
|
||||
* @return {!{connection: ?Blockly.Connection, radius: number}} Contains two
|
||||
* properties:' connection' which is either another connection or null,
|
||||
* and 'radius' which is the distance.
|
||||
*/
|
||||
Blockly.Connection.prototype.closest = function(maxLimit, dx, dy) {
|
||||
if (this.targetConnection) {
|
||||
// Don't offer to connect to a connection that's already connected.
|
||||
return {connection: null, radius: maxLimit};
|
||||
var closestConnection = this.dbOpposite_.searchForClosest(this, maxLimit, dx,
|
||||
dy);
|
||||
if (closestConnection) {
|
||||
return {connection: closestConnection,
|
||||
radius: this.distanceFrom(closestConnection)};
|
||||
}
|
||||
// Determine the opposite type of connection.
|
||||
var db = this.dbOpposite_;
|
||||
|
||||
// Since this connection is probably being dragged, add the delta.
|
||||
var currentX = this.x_ + dx;
|
||||
var currentY = this.y_ + dy;
|
||||
|
||||
// Binary search to find the closest y location.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = db.length - 2;
|
||||
var pointerMid = pointerMax;
|
||||
while (pointerMin < pointerMid) {
|
||||
if (db[pointerMid].y_ < currentY) {
|
||||
pointerMin = pointerMid;
|
||||
} else {
|
||||
pointerMax = pointerMid;
|
||||
}
|
||||
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
}
|
||||
|
||||
// Walk forward and back on the y axis looking for the closest x,y point.
|
||||
pointerMin = pointerMid;
|
||||
pointerMax = pointerMid;
|
||||
var closestConnection = null;
|
||||
var sourceBlock = this.sourceBlock_;
|
||||
var thisConnection = this;
|
||||
if (db.length) {
|
||||
while (pointerMin >= 0 && checkConnection_(pointerMin)) {
|
||||
pointerMin--;
|
||||
}
|
||||
do {
|
||||
pointerMax++;
|
||||
} while (pointerMax < db.length && checkConnection_(pointerMax));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes if the current connection is within the allowed radius of another
|
||||
* connection.
|
||||
* This function is a closure and has access to outside variables.
|
||||
* @param {number} yIndex The other connection's index in the database.
|
||||
* @return {boolean} True if the search needs to continue: either the current
|
||||
* connection's vertical distance from the other connection is less than
|
||||
* the allowed radius, or if the connection is not compatible.
|
||||
* @private
|
||||
*/
|
||||
function checkConnection_(yIndex) {
|
||||
var connection = db[yIndex];
|
||||
if (connection.type == Blockly.OUTPUT_VALUE ||
|
||||
connection.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
// Don't offer to connect an already connected left (male) value plug to
|
||||
// an available right (female) value plug. Don't offer to connect the
|
||||
// bottom of a statement block to one that's already connected.
|
||||
if (connection.targetConnection) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Offering to connect the top of a statement block to an already connected
|
||||
// connection is ok, we'll just insert it into the stack.
|
||||
|
||||
// Offering to connect the left (male) of a value block to an already
|
||||
// connected value pair is ok, we'll splice it in.
|
||||
// However, don't offer to splice into an unmovable block.
|
||||
if (connection.type == Blockly.INPUT_VALUE &&
|
||||
connection.targetConnection &&
|
||||
!connection.targetBlock().isMovable() &&
|
||||
!connection.targetBlock().isShadow()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Do type checking.
|
||||
if (!thisConnection.checkType_(connection)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Don't let blocks try to connect to themselves or ones they nest.
|
||||
var targetSourceBlock = connection.sourceBlock_;
|
||||
do {
|
||||
if (sourceBlock == targetSourceBlock) {
|
||||
return true;
|
||||
}
|
||||
targetSourceBlock = targetSourceBlock.getParent();
|
||||
} while (targetSourceBlock);
|
||||
|
||||
// Only connections within the maxLimit radius.
|
||||
var dx = currentX - connection.x_;
|
||||
var dy = currentY - connection.y_;
|
||||
var r = Math.sqrt(dx * dx + dy * dy);
|
||||
if (r <= maxLimit) {
|
||||
closestConnection = connection;
|
||||
maxLimit = r;
|
||||
}
|
||||
return Math.abs(dy) < maxLimit;
|
||||
}
|
||||
return {connection: closestConnection, radius: maxLimit};
|
||||
return {connection: null, radius: maxLimit};
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -747,7 +754,7 @@ Blockly.Connection.prototype.setCheck = function(check) {
|
|||
// The new value type may not be compatible with the existing connection.
|
||||
if (this.targetConnection && !this.checkType_(this.targetConnection)) {
|
||||
var child = this.isSuperior() ? this.targetBlock() : this.sourceBlock_;
|
||||
child.setParent(null);
|
||||
child.unplug();
|
||||
// Bump away.
|
||||
this.sourceBlock_.bumpNeighbours_();
|
||||
}
|
||||
|
@ -797,59 +804,11 @@ Blockly.Connection.prototype.getShadowDom = function() {
|
|||
* @private
|
||||
*/
|
||||
Blockly.Connection.prototype.neighbours_ = function(maxLimit) {
|
||||
// Determine the opposite type of connection.
|
||||
var db = this.dbOpposite_;
|
||||
|
||||
var currentX = this.x_;
|
||||
var currentY = this.y_;
|
||||
|
||||
// Binary search to find the closest y location.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = db.length - 2;
|
||||
var pointerMid = pointerMax;
|
||||
while (pointerMin < pointerMid) {
|
||||
if (db[pointerMid].y_ < currentY) {
|
||||
pointerMin = pointerMid;
|
||||
} else {
|
||||
pointerMax = pointerMid;
|
||||
}
|
||||
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
}
|
||||
|
||||
// Walk forward and back on the y axis looking for the closest x,y point.
|
||||
pointerMin = pointerMid;
|
||||
pointerMax = pointerMid;
|
||||
var neighbours = [];
|
||||
var sourceBlock = this.sourceBlock_;
|
||||
if (db.length) {
|
||||
while (pointerMin >= 0 && checkConnection_(pointerMin)) {
|
||||
pointerMin--;
|
||||
}
|
||||
do {
|
||||
pointerMax++;
|
||||
} while (pointerMax < db.length && checkConnection_(pointerMax));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes if the current connection is within the allowed radius of another
|
||||
* connection.
|
||||
* This function is a closure and has access to outside variables.
|
||||
* @param {number} yIndex The other connection's index in the database.
|
||||
* @return {boolean} True if the current connection's vertical distance from
|
||||
* the other connection is less than the allowed radius.
|
||||
*/
|
||||
function checkConnection_(yIndex) {
|
||||
var dx = currentX - db[yIndex].x_;
|
||||
var dy = currentY - db[yIndex].y_;
|
||||
var r = Math.sqrt(dx * dx + dy * dy);
|
||||
if (r <= maxLimit) {
|
||||
neighbours.push(db[yIndex]);
|
||||
}
|
||||
return dy < maxLimit;
|
||||
}
|
||||
return neighbours;
|
||||
return this.dbOpposite_.getNeighbours(this, maxLimit);
|
||||
};
|
||||
|
||||
// Appearance or lack thereof.
|
||||
|
||||
/**
|
||||
* Set whether this connections is hidden (not tracked in a database) or not.
|
||||
* @param {boolean} hidden True if connection is hidden.
|
||||
|
@ -859,7 +818,7 @@ Blockly.Connection.prototype.setHidden = function(hidden) {
|
|||
if (hidden && this.inDB_) {
|
||||
this.db_.removeConnection_(this);
|
||||
} else if (!hidden && !this.inDB_) {
|
||||
this.db_.addConnection_(this);
|
||||
this.db_.addConnection(this);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -929,110 +888,34 @@ Blockly.Connection.prototype.unhideAll = function() {
|
|||
return renderList;
|
||||
};
|
||||
|
||||
|
||||
/**
|
||||
* Database of connections.
|
||||
* Connections are stored in order of their vertical component. This way
|
||||
* connections in an area may be looked up quickly using a binary search.
|
||||
* @constructor
|
||||
* Add highlighting around this connection.
|
||||
*/
|
||||
Blockly.ConnectionDB = function() {
|
||||
};
|
||||
Blockly.Connection.prototype.highlight = function() {
|
||||
var steps;
|
||||
if (this.type == Blockly.INPUT_VALUE || this.type == Blockly.OUTPUT_VALUE) {
|
||||
var tabWidth = this.sourceBlock_.RTL ? -Blockly.BlockSvg.TAB_WIDTH :
|
||||
Blockly.BlockSvg.TAB_WIDTH;
|
||||
steps = 'm 0,0 ' + Blockly.BlockSvg.TAB_PATH_DOWN + ' v 5';
|
||||
|
||||
Blockly.ConnectionDB.prototype = new Array();
|
||||
/**
|
||||
* Don't inherit the constructor from Array.
|
||||
* @type {!Function}
|
||||
*/
|
||||
Blockly.ConnectionDB.constructor = Blockly.ConnectionDB;
|
||||
|
||||
/**
|
||||
* Add a connection to the database. Must not already exist in DB.
|
||||
* @param {!Blockly.Connection} connection The connection to be added.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.addConnection_ = function(connection) {
|
||||
if (connection.inDB_) {
|
||||
throw 'Connection already in database.';
|
||||
} else {
|
||||
steps = 'm -20,0 h 5 ' + Blockly.BlockSvg.NOTCH_PATH_LEFT + ' h 5';
|
||||
}
|
||||
if (connection.sourceBlock_.isInFlyout) {
|
||||
// Don't bother maintaining a database of connections in a flyout.
|
||||
return;
|
||||
}
|
||||
// Insert connection using binary search.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = this.length;
|
||||
while (pointerMin < pointerMax) {
|
||||
var pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
if (this[pointerMid].y_ < connection.y_) {
|
||||
pointerMin = pointerMid + 1;
|
||||
} else if (this[pointerMid].y_ > connection.y_) {
|
||||
pointerMax = pointerMid;
|
||||
} else {
|
||||
pointerMin = pointerMid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.splice(pointerMin, 0, connection);
|
||||
connection.inDB_ = true;
|
||||
var xy = this.sourceBlock_.getRelativeToSurfaceXY();
|
||||
var x = this.x_ - xy.x;
|
||||
var y = this.y_ - xy.y;
|
||||
Blockly.Connection.highlightedPath_ = Blockly.createSvgElement('path',
|
||||
{'class': 'blocklyHighlightedConnectionPath',
|
||||
'd': steps,
|
||||
transform: 'translate(' + x + ',' + y + ')' +
|
||||
(this.sourceBlock_.RTL ? ' scale(-1 1)' : '')},
|
||||
this.sourceBlock_.getSvgRoot());
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a connection from the database. Must already exist in DB.
|
||||
* @param {!Blockly.Connection} connection The connection to be removed.
|
||||
* @private
|
||||
* Remove the highlighting around this connection.
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.removeConnection_ = function(connection) {
|
||||
if (!connection.inDB_) {
|
||||
throw 'Connection not in database.';
|
||||
}
|
||||
connection.inDB_ = false;
|
||||
// Find the connection using a binary search.
|
||||
// About 10% faster than a linear search using indexOf.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = this.length - 2;
|
||||
var pointerMid = pointerMax;
|
||||
while (pointerMin < pointerMid) {
|
||||
if (this[pointerMid].y_ < connection.y_) {
|
||||
pointerMin = pointerMid;
|
||||
} else {
|
||||
pointerMax = pointerMid;
|
||||
}
|
||||
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
}
|
||||
|
||||
// Walk forward and back on the y axis looking for the connection.
|
||||
// When found, splice it out of the array.
|
||||
pointerMin = pointerMid;
|
||||
pointerMax = pointerMid;
|
||||
while (pointerMin >= 0 && this[pointerMin].y_ == connection.y_) {
|
||||
if (this[pointerMin] == connection) {
|
||||
this.splice(pointerMin, 1);
|
||||
return;
|
||||
}
|
||||
pointerMin--;
|
||||
}
|
||||
do {
|
||||
if (this[pointerMax] == connection) {
|
||||
this.splice(pointerMax, 1);
|
||||
return;
|
||||
}
|
||||
pointerMax++;
|
||||
} while (pointerMax < this.length &&
|
||||
this[pointerMax].y_ == connection.y_);
|
||||
throw 'Unable to find connection in connectionDB.';
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a set of connection DBs for a specified workspace.
|
||||
* @param {!Blockly.Workspace} workspace The workspace this DB is for.
|
||||
*/
|
||||
Blockly.ConnectionDB.init = function(workspace) {
|
||||
// Create four databases, one for each connection type.
|
||||
var dbList = [];
|
||||
dbList[Blockly.INPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.OUTPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.NEXT_STATEMENT] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.PREVIOUS_STATEMENT] = new Blockly.ConnectionDB();
|
||||
workspace.connectionDBList = dbList;
|
||||
Blockly.Connection.prototype.unhighlight = function() {
|
||||
goog.dom.removeNode(Blockly.Connection.highlightedPath_);
|
||||
delete Blockly.Connection.highlightedPath_;
|
||||
};
|
||||
|
|
291
core/connection_db.js
Normal file
291
core/connection_db.js
Normal file
|
@ -0,0 +1,291 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Editor
|
||||
*
|
||||
* Copyright 2011 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Components for managing connections between blocks.
|
||||
* @author fraser@google.com (Neil Fraser)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.ConnectionDB');
|
||||
|
||||
goog.require('Blockly.Connection');
|
||||
|
||||
|
||||
/**
|
||||
* Database of connections.
|
||||
* Connections are stored in order of their vertical component. This way
|
||||
* connections in an area may be looked up quickly using a binary search.
|
||||
* @constructor
|
||||
*/
|
||||
Blockly.ConnectionDB = function() {
|
||||
};
|
||||
|
||||
Blockly.ConnectionDB.prototype = new Array();
|
||||
/**
|
||||
* Don't inherit the constructor from Array.
|
||||
* @type {!Function}
|
||||
*/
|
||||
Blockly.ConnectionDB.constructor = Blockly.ConnectionDB;
|
||||
|
||||
/**
|
||||
* Add a connection to the database. Must not already exist in DB.
|
||||
* @param {!Blockly.Connection} connection The connection to be added.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.addConnection = function(connection) {
|
||||
if (connection.inDB_) {
|
||||
throw 'Connection already in database.';
|
||||
}
|
||||
if (connection.sourceBlock_.isInFlyout) {
|
||||
// Don't bother maintaining a database of connections in a flyout.
|
||||
return;
|
||||
}
|
||||
var position = this.findPositionForConnection_(connection);
|
||||
this.splice(position, 0, connection);
|
||||
connection.inDB_ = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* Find the given connection.
|
||||
* Starts by doing a binary search to find the approximate location, then
|
||||
* linearly searches nearby for the exact connection.
|
||||
* @param {Blockly.Connection} conn The connection to find.
|
||||
* @return The index of the connection, or -1 if the connection was not found.
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.findConnection = function(conn) {
|
||||
if (this.length == 0) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
var bestGuess = this.findPositionForConnection_(conn);
|
||||
if (bestGuess >= this.length) {
|
||||
// Not in list
|
||||
return -1;
|
||||
}
|
||||
|
||||
var yPos = conn.y_;
|
||||
// Walk forward and back on the y axis looking for the connection.
|
||||
var pointerMin = bestGuess;
|
||||
var pointerMax = bestGuess;
|
||||
while(pointerMin >= 0 && this[pointerMin].y_ == yPos) {
|
||||
if (this[pointerMin] == conn) {
|
||||
return pointerMin;
|
||||
}
|
||||
pointerMin--;
|
||||
}
|
||||
|
||||
while (pointerMax < this.length && this[pointerMax].y_ == yPos) {
|
||||
if (this[pointerMax] == conn) {
|
||||
return pointerMax;
|
||||
}
|
||||
pointerMax++;
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
|
||||
/**
|
||||
* Finds a candidate position for inserting this connection into the list.
|
||||
* This will be in the correct y order but makes no guarantees about ordering in
|
||||
* the x axis.
|
||||
* @param {Blockly.Connection} connection The connection to insert.
|
||||
* @return {number} The candidate index.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.findPositionForConnection_ = function(connection) {
|
||||
if (this.length == 0) {
|
||||
return 0;
|
||||
}
|
||||
var pointerMin = 0;
|
||||
var pointerMax = this.length;
|
||||
while (pointerMin < pointerMax) {
|
||||
var pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
if (this[pointerMid].y_ < connection.y_) {
|
||||
pointerMin = pointerMid + 1;
|
||||
} else if (this[pointerMid].y_ > connection.y_) {
|
||||
pointerMax = pointerMid;
|
||||
} else {
|
||||
pointerMin = pointerMid;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return pointerMin;
|
||||
};
|
||||
|
||||
/**
|
||||
* Remove a connection from the database. Must already exist in DB.
|
||||
* @param {!Blockly.Connection} connection The connection to be removed.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.removeConnection_ = function(connection) {
|
||||
if (!connection.inDB_) {
|
||||
throw 'Connection not in database.';
|
||||
}
|
||||
var removalIndex = this.findConnection(connection);
|
||||
if (removalIndex == -1) {
|
||||
throw 'Unable to find connection in connectionDB.';
|
||||
}
|
||||
connection.inDB_ = false;
|
||||
this.splice(removalIndex, 1);
|
||||
};
|
||||
|
||||
/**
|
||||
* Find all nearby connections to the given connection.
|
||||
* Type checking does not apply, since this function is used for bumping.
|
||||
* @param {!Blockly.Connection} connection The connection whose neighbours should
|
||||
* be returned.
|
||||
* @param {number} maxRadius The maximum radius to another connection.
|
||||
* @return {!Array.<Blockly.Connection>} List of connections.
|
||||
* @private
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.getNeighbours = function(connection, maxRadius) {
|
||||
var db = this;
|
||||
var currentX = connection.x_;
|
||||
var currentY = connection.y_;
|
||||
|
||||
// Binary search to find the closest y location.
|
||||
var pointerMin = 0;
|
||||
var pointerMax = db.length - 2;
|
||||
var pointerMid = pointerMax;
|
||||
while (pointerMin < pointerMid) {
|
||||
if (db[pointerMid].y_ < currentY) {
|
||||
pointerMin = pointerMid;
|
||||
} else {
|
||||
pointerMax = pointerMid;
|
||||
}
|
||||
pointerMid = Math.floor((pointerMin + pointerMax) / 2);
|
||||
}
|
||||
|
||||
// Walk forward and back on the y axis looking for the closest x,y point.
|
||||
pointerMin = pointerMid;
|
||||
pointerMax = pointerMid;
|
||||
var neighbours = [];
|
||||
var sourceBlock = connection.sourceBlock_;
|
||||
if (db.length) {
|
||||
while (pointerMin >= 0 && checkConnection_(pointerMin)) {
|
||||
pointerMin--;
|
||||
}
|
||||
do {
|
||||
pointerMax++;
|
||||
} while (pointerMax < db.length && checkConnection_(pointerMax));
|
||||
}
|
||||
|
||||
/**
|
||||
* Computes if the current connection is within the allowed radius of another
|
||||
* connection.
|
||||
* This function is a closure and has access to outside variables.
|
||||
* @param {number} yIndex The other connection's index in the database.
|
||||
* @return {boolean} True if the current connection's vertical distance from
|
||||
* the other connection is less than the allowed radius.
|
||||
*/
|
||||
function checkConnection_(yIndex) {
|
||||
var dx = currentX - db[yIndex].x_;
|
||||
var dy = currentY - db[yIndex].y_;
|
||||
var r = Math.sqrt(dx * dx + dy * dy);
|
||||
if (r <= maxRadius) {
|
||||
neighbours.push(db[yIndex]);
|
||||
}
|
||||
return dy < maxRadius;
|
||||
}
|
||||
return neighbours;
|
||||
};
|
||||
|
||||
|
||||
Blockly.ConnectionDB.prototype.isInYRange_ = function(index, baseY, maxRadius) {
|
||||
return (Math.abs(this[index].y_ - baseY) <= maxRadius);
|
||||
}
|
||||
|
||||
/**
|
||||
* Find the closest compatible connection to this connection.
|
||||
* @param {Blockly.Connection} conn The connection searching for a compatible
|
||||
* mate.
|
||||
* @param {number} maxRadius The maximum radius to another connection.
|
||||
* @param {number} dx Horizontal offset between this connection's location
|
||||
* in the database and the current location (as a result of dragging).
|
||||
* @param {number} dy Vertical offset between this connection's location
|
||||
* in the database and the current location (as a result of dragging).
|
||||
* @return ?Blockly.Connection the closest valid connection.
|
||||
* another connection or null, and 'radius' which is the distance.
|
||||
*/
|
||||
Blockly.ConnectionDB.prototype.searchForClosest = function(conn, maxRadius, dx,
|
||||
dy) {
|
||||
// Don't bother.
|
||||
if (!this.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Stash the values of x and y from before the drag.
|
||||
var baseY = conn.y_;
|
||||
var baseX = conn.x_;
|
||||
|
||||
conn.x_ = baseX + dx;
|
||||
conn.y_ = baseY + dy;
|
||||
|
||||
// findPositionForConnection finds an index for insertion, which is always
|
||||
// after any block with the same y index. We want to search both forward
|
||||
// and back, so search on both sides of the index.
|
||||
var closestIndex = this.findPositionForConnection_(conn);
|
||||
|
||||
var bestConnection = null;
|
||||
var bestRadius = maxRadius;
|
||||
var temp;
|
||||
|
||||
// Walk forward and back on the y axis looking for the closest x,y point.
|
||||
var pointerMin = closestIndex - 1;
|
||||
while (pointerMin >= 0 &&
|
||||
this.isInYRange_(pointerMin, conn.y_, maxRadius)) {
|
||||
temp = this[pointerMin];
|
||||
if (conn.isConnectionAllowed(temp, bestRadius)) {
|
||||
bestConnection = temp;
|
||||
bestRadius = temp.distanceFrom(conn);
|
||||
}
|
||||
pointerMin--;
|
||||
}
|
||||
|
||||
var pointerMax = closestIndex;
|
||||
while (pointerMax < this.length && this.isInYRange_(pointerMax, conn.y_,
|
||||
maxRadius)) {
|
||||
temp = this[pointerMax];
|
||||
if (conn.isConnectionAllowed(temp, bestRadius)) {
|
||||
bestConnection = temp;
|
||||
bestRadius = temp.distanceFrom(conn);
|
||||
}
|
||||
pointerMax++;
|
||||
}
|
||||
|
||||
// Reset the values of x and y.
|
||||
conn.x_ = baseX;
|
||||
conn.y_ = baseY;
|
||||
return bestConnection;
|
||||
};
|
||||
|
||||
/**
|
||||
* Initialize a set of connection DBs for a specified workspace.
|
||||
* @param {!Blockly.Workspace} workspace The workspace this DB is for.
|
||||
*/
|
||||
Blockly.ConnectionDB.init = function(workspace) {
|
||||
// Create four databases, one for each connection type.
|
||||
var dbList = [];
|
||||
dbList[Blockly.INPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.OUTPUT_VALUE] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.NEXT_STATEMENT] = new Blockly.ConnectionDB();
|
||||
dbList[Blockly.PREVIOUS_STATEMENT] = new Blockly.ConnectionDB();
|
||||
workspace.connectionDBList = dbList;
|
||||
};
|
177
core/events.js
177
core/events.js
|
@ -30,8 +30,15 @@ goog.provide('Blockly.Events');
|
|||
/**
|
||||
* Group ID for new events. Grouped events are indivisible.
|
||||
* @type {string}
|
||||
* @private
|
||||
*/
|
||||
Blockly.Events.group = '';
|
||||
Blockly.Events.group_ = '';
|
||||
|
||||
/**
|
||||
* Sets whether events should be added to the undo stack.
|
||||
* @type {boolean}
|
||||
*/
|
||||
Blockly.Events.recordUndo = true;
|
||||
|
||||
/**
|
||||
* Allow change events to be created and fired.
|
||||
|
@ -90,7 +97,7 @@ Blockly.Events.fire = function(event) {
|
|||
* @private
|
||||
*/
|
||||
Blockly.Events.fireNow_ = function() {
|
||||
var queue = Blockly.Events.filter_(Blockly.Events.FIRE_QUEUE_);
|
||||
var queue = Blockly.Events.filter(Blockly.Events.FIRE_QUEUE_);
|
||||
Blockly.Events.FIRE_QUEUE_.length = 0;
|
||||
for (var i = 0, event; event = queue[i]; i++) {
|
||||
var workspace = Blockly.Workspace.getById(event.workspaceId);
|
||||
|
@ -104,9 +111,8 @@ Blockly.Events.fireNow_ = function() {
|
|||
* Filter the queued events and merge duplicates.
|
||||
* @param {!Array.<!Blockly.Events.Abstract>} queueIn Array of events.
|
||||
* @return {!Array.<!Blockly.Events.Abstract>} Array of filtered events.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Events.filter_ = function(queueIn) {
|
||||
Blockly.Events.filter = function(queueIn) {
|
||||
var queue = goog.array.clone(queueIn);
|
||||
// Merge duplicates. O(n^2), but n should be very small.
|
||||
for (var i = 0, event1; event1 = queue[i]; i++) {
|
||||
|
@ -138,6 +144,14 @@ Blockly.Events.filter_ = function(queueIn) {
|
|||
queue.splice(i, 1);
|
||||
}
|
||||
}
|
||||
// Move mutation events to the top of the queue.
|
||||
// Intentionally skip first event.
|
||||
for (var i = 1, event; event = queue[i]; i++) {
|
||||
if (event.type == Blockly.Events.CHANGE &&
|
||||
event.element == 'mutation') {
|
||||
queue.unshift(queue.splice(i, 1)[0]);
|
||||
}
|
||||
}
|
||||
return queue;
|
||||
};
|
||||
|
||||
|
@ -164,6 +178,27 @@ Blockly.Events.isEnabled = function() {
|
|||
return Blockly.Events.disabled_ == 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* Current group.
|
||||
* @return {string} ID string.
|
||||
*/
|
||||
Blockly.Events.getGroup = function() {
|
||||
return Blockly.Events.group_;
|
||||
};
|
||||
|
||||
/**
|
||||
* Start or stop a group.
|
||||
* @param {boolean|string} state True to start new group, false to end group.
|
||||
* String to set group explicitly.
|
||||
*/
|
||||
Blockly.Events.setGroup = function(state) {
|
||||
if (typeof state == 'boolean') {
|
||||
Blockly.Events.group_ = state ? Blockly.genUid() : '';
|
||||
} else {
|
||||
Blockly.Events.group_ = state;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Abstract class for an event.
|
||||
* @param {!Blockly.Block} block The block.
|
||||
|
@ -172,7 +207,8 @@ Blockly.Events.isEnabled = function() {
|
|||
Blockly.Events.Abstract = function(block) {
|
||||
this.blockId = block.id;
|
||||
this.workspaceId = block.workspace.id;
|
||||
this.group = Blockly.Events.group;
|
||||
this.group = Blockly.Events.group_;
|
||||
this.recordUndo = Blockly.Events.recordUndo;
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -201,6 +237,24 @@ goog.inherits(Blockly.Events.Create, Blockly.Events.Abstract);
|
|||
*/
|
||||
Blockly.Events.Create.prototype.type = Blockly.Events.CREATE;
|
||||
|
||||
/**
|
||||
* Run a creation event.
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.Create.prototype.run = function(forward) {
|
||||
if (forward) {
|
||||
var workspace = Blockly.Workspace.getById(this.workspaceId);
|
||||
var xml = goog.dom.createDom('xml');
|
||||
xml.appendChild(this.xml);
|
||||
Blockly.Xml.domToWorkspace(workspace, xml);
|
||||
} else {
|
||||
var block = Blockly.Block.getById(this.blockId);
|
||||
if (block) {
|
||||
block.dispose(false, true);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for a block deletion event.
|
||||
* @param {!Blockly.Block} block The deleted block.
|
||||
|
@ -222,6 +276,24 @@ goog.inherits(Blockly.Events.Delete, Blockly.Events.Abstract);
|
|||
*/
|
||||
Blockly.Events.Delete.prototype.type = Blockly.Events.DELETE;
|
||||
|
||||
/**
|
||||
* Run a deletion event.
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.Delete.prototype.run = function(forward) {
|
||||
if (forward) {
|
||||
var block = Blockly.Block.getById(this.blockId);
|
||||
if (block) {
|
||||
block.dispose(false, true);
|
||||
}
|
||||
} else {
|
||||
var workspace = Blockly.Workspace.getById(this.workspaceId);
|
||||
var xml = goog.dom.createDom('xml');
|
||||
xml.appendChild(this.oldXml);
|
||||
Blockly.Xml.domToWorkspace(workspace, xml);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for a block change event.
|
||||
* @param {!Blockly.Block} block The changed block.
|
||||
|
@ -255,6 +327,57 @@ Blockly.Events.Change.prototype.isNull = function() {
|
|||
return this.oldValue == this.newValue;
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a change event.
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.Change.prototype.run = function(forward) {
|
||||
var block = Blockly.Block.getById(this.blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
var value = forward ? this.newValue : this.oldValue;
|
||||
switch (this.element) {
|
||||
case 'field':
|
||||
var field = block.getField(this.name);
|
||||
if (field) {
|
||||
field.setValue(value);
|
||||
} else {
|
||||
console.warn("Can't set non-existant field: " + this.name);
|
||||
}
|
||||
break;
|
||||
case 'comment':
|
||||
block.setCommentText(value || null);
|
||||
break;
|
||||
case 'collapsed':
|
||||
block.setCollapsed(value);
|
||||
break;
|
||||
case 'disabled':
|
||||
block.setDisabled(value);
|
||||
break;
|
||||
case 'inline':
|
||||
block.setInputsInline(value);
|
||||
break;
|
||||
case 'mutation':
|
||||
if (block.mutator) {
|
||||
// Close the mutator (if open) since we don't want to update it.
|
||||
block.mutator.setVisible(false);
|
||||
}
|
||||
var oldMutation = '';
|
||||
if (block.mutationToDom) {
|
||||
var oldMutationDom = block.mutationToDom();
|
||||
oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
|
||||
}
|
||||
if (block.domToMutation) {
|
||||
var dom = Blockly.Xml.textToDom('<xml>' + value + '</xml>');
|
||||
block.domToMutation(dom.firstChild);
|
||||
}
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
block, 'mutation', null, oldMutation, value));
|
||||
break;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Class for a block move event. Created before the move.
|
||||
* @param {!Blockly.Block} block The moved block.
|
||||
|
@ -317,3 +440,47 @@ Blockly.Events.Move.prototype.isNull = function() {
|
|||
this.oldInputName == this.newInputName &&
|
||||
goog.math.Coordinate.equals(this.oldCoordinate, this.newCoordinate);
|
||||
};
|
||||
|
||||
/**
|
||||
* Run a move event.
|
||||
* @param {boolean} forward True if run forward, false if run backward (undo).
|
||||
*/
|
||||
Blockly.Events.Move.prototype.run = function(forward) {
|
||||
var block = Blockly.Block.getById(this.blockId);
|
||||
if (!block) {
|
||||
return;
|
||||
}
|
||||
var parentId = forward ? this.newParentId : this.oldParentId;
|
||||
var inputName = forward ? this.newInputName : this.oldInputName;
|
||||
var coordinate = forward ? this.newCoordinate : this.oldCoordinate;
|
||||
var parentBlock = null;
|
||||
if (parentId) {
|
||||
parentBlock = Blockly.Block.getById(parentId);
|
||||
if (!parentBlock) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (block.getParent()) {
|
||||
block.unplug();
|
||||
}
|
||||
if (coordinate) {
|
||||
var xy = block.getRelativeToSurfaceXY();
|
||||
block.moveBy(coordinate.x - xy.x, coordinate.y - xy.y);
|
||||
} else {
|
||||
var blockConnection = block.outputConnection || block.previousConnection;
|
||||
var parentConnection;
|
||||
if (inputName) {
|
||||
var input = parentBlock.getInput(inputName);
|
||||
if (input) {
|
||||
parentConnection = input.connection;
|
||||
} else {
|
||||
console.warn("Can't connect to non-existant input: " + inputName);
|
||||
}
|
||||
} else if (blockConnection.type == Blockly.PREVIOUS_STATEMENT) {
|
||||
parentConnection = parentBlock.nextConnection;
|
||||
}
|
||||
if (parentConnection) {
|
||||
blockConnection.connect(parentConnection);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
|
|
@ -812,7 +812,8 @@ Blockly.Flyout.prototype.createBlockFunc_ = function(originBlock) {
|
|||
}
|
||||
block.moveBy(xyOld.x - xyNew.x, xyOld.y - xyNew.y);
|
||||
Blockly.Events.enable();
|
||||
if (Blockly.Events.isEnabled() && !block.isShadow()) {
|
||||
if (Blockly.Events.isEnabled()) {
|
||||
Blockly.Events.setGroup(true);
|
||||
Blockly.Events.fire(new Blockly.Events.Create(block));
|
||||
}
|
||||
if (flyout.autoClose) {
|
||||
|
|
|
@ -280,29 +280,36 @@ Blockly.Mutator.prototype.workspaceChanged_ = function() {
|
|||
|
||||
// When the mutator's workspace changes, update the source block.
|
||||
if (this.rootBlock_.workspace == this.workspace_) {
|
||||
var oldMutationDom = this.block_.mutationToDom();
|
||||
Blockly.Events.setGroup(true);
|
||||
var block = this.block_;
|
||||
var oldMutationDom = block.mutationToDom();
|
||||
var oldMutation = oldMutationDom && Blockly.Xml.domToText(oldMutationDom);
|
||||
// Switch off rendering while the source block is rebuilt.
|
||||
var savedRendered = this.block_.rendered;
|
||||
this.block_.rendered = false;
|
||||
var savedRendered = block.rendered;
|
||||
block.rendered = false;
|
||||
// Allow the source block to rebuild itself.
|
||||
this.block_.compose(this.rootBlock_);
|
||||
block.compose(this.rootBlock_);
|
||||
// Restore rendering and show the changes.
|
||||
this.block_.rendered = savedRendered;
|
||||
block.rendered = savedRendered;
|
||||
// Mutation may have added some elements that need initalizing.
|
||||
this.block_.initSvg();
|
||||
var newMutationDom = this.block_.mutationToDom();
|
||||
block.initSvg();
|
||||
var newMutationDom = block.mutationToDom();
|
||||
var newMutation = newMutationDom && Blockly.Xml.domToText(newMutationDom);
|
||||
if (oldMutation != newMutation) {
|
||||
Blockly.Events.fire(new Blockly.Events.Change(
|
||||
this.block_, 'mutation', null, oldMutation, newMutation));
|
||||
goog.Timer.callOnce(
|
||||
this.block_.bumpNeighbours_, Blockly.BUMP_DELAY, this.block_);
|
||||
block, 'mutation', null, oldMutation, newMutation));
|
||||
// Ensure that any bump is part of this mutation's event group.
|
||||
var group = Blockly.Events.getGroup();
|
||||
setTimeout(function() {
|
||||
Blockly.Events.setGroup(group);
|
||||
block.bumpNeighbours_();
|
||||
}, Blockly.BUMP_DELAY);
|
||||
}
|
||||
if (this.block_.rendered) {
|
||||
this.block_.render();
|
||||
if (block.rendered) {
|
||||
block.render();
|
||||
}
|
||||
this.resizeBubble_();
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -158,7 +158,7 @@ Blockly.ScrollbarPair.prototype.set = function(x, y) {
|
|||
this.workspace_.setMetrics(xyRatio);
|
||||
this.hScroll.svgKnob_.setAttribute('x', hKnobValue);
|
||||
this.vScroll.svgKnob_.setAttribute('y', vKnobValue);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Helper to calculate the ratio of knob value to bar length.
|
||||
|
@ -172,7 +172,7 @@ Blockly.ScrollbarPair.prototype.getRatio_ = function(knobValue, barLength) {
|
|||
return 0;
|
||||
}
|
||||
return ratio;
|
||||
}
|
||||
};
|
||||
|
||||
// --------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -80,11 +80,13 @@ Blockly.Variables.allVariables = function(root) {
|
|||
* @param {!Blockly.Workspace} workspace Workspace rename variables in.
|
||||
*/
|
||||
Blockly.Variables.renameVariable = function(oldName, newName, workspace) {
|
||||
Blockly.Events.setGroup(true);
|
||||
var blocks = workspace.getAllBlocks();
|
||||
// Iterate through every block.
|
||||
for (var i = 0; i < blocks.length; i++) {
|
||||
blocks[i].renameVar(oldName, newName);
|
||||
}
|
||||
Blockly.Events.setGroup(false);
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
@ -82,6 +82,7 @@ Blockly.WidgetDiv.show = function(newOwner, rtl, dispose) {
|
|||
Blockly.WidgetDiv.DIV.style.top = xy.y + 'px';
|
||||
Blockly.WidgetDiv.DIV.style.direction = rtl ? 'rtl' : 'ltr';
|
||||
Blockly.WidgetDiv.DIV.style.display = 'block';
|
||||
Blockly.Events.setGroup(true);
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -97,6 +98,7 @@ Blockly.WidgetDiv.hide = function() {
|
|||
Blockly.WidgetDiv.dispose_ && Blockly.WidgetDiv.dispose_();
|
||||
Blockly.WidgetDiv.dispose_ = null;
|
||||
goog.dom.removeChildren(Blockly.WidgetDiv.DIV);
|
||||
Blockly.Events.setGroup(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -51,6 +51,10 @@ Blockly.Workspace = function(opt_options) {
|
|||
this.listeners_ = [];
|
||||
/** @type {!Array.<!Function>} */
|
||||
this.tapListeners_ = [];
|
||||
/** @type {!Array.<!Blockly.Events.Abstract>} */
|
||||
this.undoStack_ = [];
|
||||
/** @type {!Array.<!Blockly.Events.Abstract>} */
|
||||
this.redoStack_ = [];
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -59,6 +63,12 @@ Blockly.Workspace = function(opt_options) {
|
|||
*/
|
||||
Blockly.Workspace.prototype.rendered = false;
|
||||
|
||||
/**
|
||||
* Maximum number of undo events in stack.
|
||||
* @type {number} 0 to turn off undo, Infinity for unlimited.
|
||||
*/
|
||||
Blockly.Workspace.prototype.MAX_UNDO = 1024;
|
||||
|
||||
/**
|
||||
* Dispose of this workspace.
|
||||
* Unlink from all DOM elements to prevent memory leaks.
|
||||
|
@ -198,6 +208,31 @@ Blockly.Workspace.prototype.remainingCapacity = function() {
|
|||
return this.options.maxBlocks - this.getAllBlocks().length;
|
||||
};
|
||||
|
||||
/**
|
||||
* Undo or redo the previous action.
|
||||
* @param {boolean} redo False if undo, true if redo.
|
||||
*/
|
||||
Blockly.Workspace.prototype.undo = function(redo) {
|
||||
var sourceStack = redo ? this.redoStack_ : this.undoStack_;
|
||||
var event = sourceStack.pop();
|
||||
if (!event) {
|
||||
return;
|
||||
}
|
||||
var events = [event];
|
||||
// Do another undo/redo if the next one is of the same group.
|
||||
while (sourceStack.length && event.group &&
|
||||
event.group == sourceStack[sourceStack.length - 1].group) {
|
||||
events.push(sourceStack.pop());
|
||||
}
|
||||
events = Blockly.Events.filter(events);
|
||||
Blockly.Events.recordUndo = false;
|
||||
for (var i = 0, event; event = events[i]; i++) {
|
||||
event.run(redo);
|
||||
(redo ? this.undoStack_ : this.redoStack_).push(event);
|
||||
}
|
||||
Blockly.Events.recordUndo = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* When something in this workspace changes, call a function.
|
||||
* @param {!Function} func Function to call.
|
||||
|
@ -225,6 +260,13 @@ Blockly.Workspace.prototype.removeChangeListener = function(func) {
|
|||
* @param {!Blockly.Events.Abstract} event Event to fire.
|
||||
*/
|
||||
Blockly.Workspace.prototype.fireChangeListener = function(event) {
|
||||
if (event.recordUndo) {
|
||||
this.undoStack_.push(event);
|
||||
this.redoStack_.length = 0;
|
||||
if (this.undoStack_.length > this.MAX_UNDO) {
|
||||
this.undoStack_.unshift();
|
||||
}
|
||||
}
|
||||
for (var i = 0, func; func = this.listeners_[i]; i++) {
|
||||
func(event);
|
||||
}
|
||||
|
|
|
@ -663,6 +663,7 @@ Blockly.WorkspaceSvg.prototype.onMouseWheel_ = function(e) {
|
|||
* @private
|
||||
*/
|
||||
Blockly.WorkspaceSvg.prototype.cleanUp_ = function() {
|
||||
Blockly.Events.setGroup(true);
|
||||
var topBlocks = this.getTopBlocks(true);
|
||||
var cursorY = 0;
|
||||
for (var i = 0, block; block = topBlocks[i]; i++) {
|
||||
|
@ -672,6 +673,7 @@ Blockly.WorkspaceSvg.prototype.cleanUp_ = function() {
|
|||
cursorY = block.getRelativeToSurfaceXY().y +
|
||||
block.getHeightWidth().height + Blockly.BlockSvg.MIN_BLOCK_Y;
|
||||
}
|
||||
Blockly.Events.setGroup(false);
|
||||
// Fire an event to allow scrollbars to resize.
|
||||
Blockly.fireUiEvent(window, 'resize');
|
||||
};
|
||||
|
|
|
@ -142,7 +142,7 @@
|
|||
<select id="format">
|
||||
<option value="JavaScript">JavaScript</option>
|
||||
<option value="JSON">JSON</option>
|
||||
<option value="Manual">Manual edit...</option>
|
||||
<option value="Manual">Manual edit…</option>
|
||||
</select>
|
||||
</h3>
|
||||
</td>
|
||||
|
@ -161,6 +161,7 @@
|
|||
<option value="Python">Python</option>
|
||||
<option value="PHP">PHP</option>
|
||||
<option value="Dart">Dart</option>
|
||||
<option value="Lua">Lua</option>
|
||||
</select>
|
||||
</h3>
|
||||
</td>
|
||||
|
|
|
@ -244,7 +244,7 @@ Code.LANG = Code.getLang();
|
|||
* List of tab names.
|
||||
* @private
|
||||
*/
|
||||
Code.TABS_ = ['blocks', 'javascript', 'php', 'python', 'dart', 'xml'];
|
||||
Code.TABS_ = ['blocks', 'javascript', 'php', 'python', 'dart', 'lua', 'xml'];
|
||||
|
||||
Code.selected = 'blocks';
|
||||
|
||||
|
@ -341,6 +341,14 @@ Code.renderContent = function() {
|
|||
code = prettyPrintOne(code, 'dart');
|
||||
content.innerHTML = code;
|
||||
}
|
||||
} else if (content.id == 'content_lua') {
|
||||
code = Blockly.Lua.workspaceToCode(Code.workspace);
|
||||
content.textContent = code;
|
||||
if (typeof prettyPrintOne == 'function') {
|
||||
code = content.innerHTML;
|
||||
code = prettyPrintOne(code, 'lua');
|
||||
content.innerHTML = code;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
<script src="../../python_compressed.js"></script>
|
||||
<script src="../../php_compressed.js"></script>
|
||||
<script src="../../dart_compressed.js"></script>
|
||||
<script src="../../lua_compressed.js"></script>
|
||||
<script src="code.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
|
@ -41,6 +42,8 @@
|
|||
<td class="tabmin"> </td>
|
||||
<td id="tab_dart" class="taboff">Dart</td>
|
||||
<td class="tabmin"> </td>
|
||||
<td id="tab_lua" class="taboff">Lua</td>
|
||||
<td class="tabmin"> </td>
|
||||
<td id="tab_xml" class="taboff">XML</td>
|
||||
<td class="tabmax">
|
||||
<button id="trashButton" class="notext" title="...">
|
||||
|
@ -67,6 +70,7 @@
|
|||
<pre id="content_php" class="content"></pre>
|
||||
<pre id="content_python" class="content"></pre>
|
||||
<pre id="content_dart" class="content"></pre>
|
||||
<pre id="content_lua" class="content"></pre>
|
||||
<textarea id="content_xml" class="content" wrap="off"></textarea>
|
||||
|
||||
<xml id="toolbox" style="display: none">
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
<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/installation/injecting-fixed-size">injecting fixed-sized Blockly</a>...</p>
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/injecting-fixed-size">injecting fixed-sized Blockly</a>…</p>
|
||||
|
||||
<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>
|
||||
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
<p>This is a simple demo of generating code from blocks.</p>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/code-generators">Code Generators</a>...</p>
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/code-generators">Code Generators</a>…</p>
|
||||
|
||||
<p>
|
||||
<button onclick="showCode()">Show JavaScript</button>
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
|
||||
<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/installation/code-generators#realtime_generation">Realtime generation</a>...</p>
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/code-generators#realtime_generation">Realtime generation</a>…</p>
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
|
|
|
@ -168,7 +168,7 @@
|
|||
</td>
|
||||
<td>
|
||||
<div><a href="code/index.html">Code Editor</a></div>
|
||||
<div>Export a Blockly program into JavaScript, Python, PHP, Dart or XML.</div>
|
||||
<div>Export a Blockly program into JavaScript, Python, PHP, Dart, Lua or XML.</div>
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@
|
|||
|
||||
<p>This is a simple demo of executing code with a sandboxed JavaScript interpreter.</p>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/js-interpreter">JS Interpreter</a>...</p>
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/js-interpreter">JS Interpreter</a>…</p>
|
||||
|
||||
<p>
|
||||
<button onclick="parseCode()">Parse JavaScript</button>
|
||||
|
|
|
@ -39,7 +39,7 @@
|
|||
CSS or tables to create an area for it.
|
||||
Next, <a href="overlay.html">inject Blockly</a> over that area.</p>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/injecting-resizable">injecting resizable Blockly</a>...</p>
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/injecting-resizable">injecting resizable Blockly</a>…</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
A resize handler keeps it in position as the page changes.
|
||||
</p>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/injecting-resizable">injecting resizable Blockly</a>...</p>
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/injecting-resizable">injecting resizable Blockly</a>…</p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
|
|
|
@ -41,7 +41,7 @@
|
|||
}
|
||||
</script>
|
||||
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/cloud-storage">Cloud Storage</a>...</p>
|
||||
<p>→ More info on <a href="https://developers.google.com/blockly/installation/cloud-storage">Cloud Storage</a>…</p>
|
||||
|
||||
<p>
|
||||
<button onclick="BlocklyStorage.link()">Save Blocks</button>
|
||||
|
|
|
@ -23,7 +23,7 @@
|
|||
|
||||
<p>This is a demo of a complex category structure for the toolbox.</p>
|
||||
|
||||
<p>→ More info on the <a href="https://developers.google.com/blockly/installation/toolbox">Toolbox</a>...</p>
|
||||
<p>→ More info on the <a href="https://developers.google.com/blockly/installation/toolbox">Toolbox</a>…</p>
|
||||
|
||||
<div id="blocklyDiv" style="height: 600px; width: 800px;"></div>
|
||||
|
||||
|
|
188
generators/lua.js
Normal file
188
generators/lua.js
Normal file
|
@ -0,0 +1,188 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Helper functions for generating Lua for blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
* Based on Ellen Spertus's blocky-lua project.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua');
|
||||
|
||||
goog.require('Blockly.Generator');
|
||||
|
||||
|
||||
/**
|
||||
* Lua code generator.
|
||||
* @type {!Blockly.Generator}
|
||||
*/
|
||||
Blockly.Lua = new Blockly.Generator('Lua');
|
||||
|
||||
/**
|
||||
* List of illegal variable names.
|
||||
* This is not intended to be a security feature. Blockly is 100% client-side,
|
||||
* so bypassing this list is trivial. This is intended to prevent users from
|
||||
* accidentally clobbering a built-in object or function.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Lua.addReservedWords(
|
||||
// Special character
|
||||
'_' +
|
||||
// From theoriginalbit's script:
|
||||
// https://github.com/espertus/blockly-lua/issues/6
|
||||
'__inext,assert,bit,colors,colours,coroutine,disk,dofile,error,fs,' +
|
||||
'fetfenv,getmetatable,gps,help,io,ipairs,keys,loadfile,loadstring,math,' +
|
||||
'native,next,os,paintutils,pairs,parallel,pcall,peripheral,print,' +
|
||||
'printError,rawequal,rawget,rawset,read,rednet,redstone,rs,select,' +
|
||||
'setfenv,setmetatable,sleep,string,table,term,textutils,tonumber,' +
|
||||
'tostring,turtle,type,unpack,vector,write,xpcall,_VERSION,__indext,' +
|
||||
// Not included in the script, probably because it wasn't enabled:
|
||||
'HTTP,' +
|
||||
// Keywords (http://www.lua.org/pil/1.3.html).
|
||||
'and,break,do,else,elseif,end,false,for,function,if,in,local,nil,not,or,' +
|
||||
'repeat,return,then,true,until,while,' +
|
||||
// Metamethods (http://www.lua.org/manual/5.2/manual.html).
|
||||
'add,sub,mul,div,mod,pow,unm,concat,len,eq,lt,le,index,newindex,call,' +
|
||||
// Basic functions (http://www.lua.org/manual/5.2/manual.html, section 6.1).
|
||||
'assert,collectgarbage,dofile,error,_G,getmetatable,inpairs,load,' +
|
||||
'loadfile,next,pairs,pcall,print,rawequal,rawget,rawlen,rawset,select,' +
|
||||
'setmetatable,tonumber,tostring,type,_VERSION,xpcall,' +
|
||||
// Modules (http://www.lua.org/manual/5.2/manual.html, section 6.3).
|
||||
'require,package,string,table,math,bit32,io,file,os,debug'
|
||||
);
|
||||
|
||||
/**
|
||||
* Order of operation ENUMs.
|
||||
* http://www.lua.org/manual/5.3/manual.html#3.4.8
|
||||
*/
|
||||
Blockly.Lua.ORDER_ATOMIC = 0; // literals
|
||||
// The next level was not explicit in documentation and inferred by Ellen.
|
||||
Blockly.Lua.ORDER_HIGH = 1; // Function calls, tables[]
|
||||
Blockly.Lua.ORDER_EXPONENTIATION = 2; // ^
|
||||
Blockly.Lua.ORDER_UNARY = 3; // not # - ()
|
||||
Blockly.Lua.ORDER_MULTIPLICATIVE = 4; // * / %
|
||||
Blockly.Lua.ORDER_ADDITIVE = 5; // + -
|
||||
Blockly.Lua.ORDER_CONCATENATION = 6; // ..
|
||||
Blockly.Lua.ORDER_RELATIONAL = 7; // < > <= >= ~= ==
|
||||
Blockly.Lua.ORDER_AND = 8; // and
|
||||
Blockly.Lua.ORDER_OR = 9; // or
|
||||
Blockly.Lua.ORDER_NONE = 99;
|
||||
|
||||
/**
|
||||
* Initialise the database of variable names.
|
||||
* @param {!Blockly.Workspace} workspace Workspace to generate code from.
|
||||
*/
|
||||
Blockly.Lua.init = function(workspace) {
|
||||
// Create a dictionary of definitions to be printed before the code.
|
||||
Blockly.Lua.definitions_ = Object.create(null);
|
||||
// Create a dictionary mapping desired function names in definitions_
|
||||
// to actual function names (to avoid collisions with user functions).
|
||||
Blockly.Lua.functionNames_ = Object.create(null);
|
||||
|
||||
if (!Blockly.Lua.variableDB_) {
|
||||
Blockly.Lua.variableDB_ =
|
||||
new Blockly.Names(Blockly.Lua.RESERVED_WORDS_);
|
||||
} else {
|
||||
Blockly.Lua.variableDB_.reset();
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Prepend the generated code with the variable definitions.
|
||||
* @param {string} code Generated code.
|
||||
* @return {string} Completed code.
|
||||
*/
|
||||
Blockly.Lua.finish = function(code) {
|
||||
// Convert the definitions dictionary into a list.
|
||||
var definitions = [];
|
||||
for (var name in Blockly.Lua.definitions_) {
|
||||
definitions.push(Blockly.Lua.definitions_[name]);
|
||||
}
|
||||
// Clean up temporary data.
|
||||
delete Blockly.Lua.definitions_;
|
||||
delete Blockly.Lua.functionNames_;
|
||||
Blockly.Lua.variableDB_.reset();
|
||||
return definitions.join('\n\n') + '\n\n\n' + code;
|
||||
};
|
||||
|
||||
/**
|
||||
* Naked values are top-level blocks with outputs that aren't plugged into
|
||||
* anything. In Lua, an expression is not a legal statement, so we must assign
|
||||
* the value to the (conventionally ignored) _.
|
||||
* http://lua-users.org/wiki/ExpressionsAsStatements
|
||||
* @param {string} line Line of generated code.
|
||||
* @return {string} Legal line of code.
|
||||
*/
|
||||
Blockly.Lua.scrubNakedValue = function(line) {
|
||||
return 'local _ = ' + line + '\n';
|
||||
};
|
||||
|
||||
/**
|
||||
* Encode a string as a properly escaped Lua string, complete with
|
||||
* quotes.
|
||||
* @param {string} string Text to encode.
|
||||
* @return {string} Lua string.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Lua.quote_ = function(string) {
|
||||
// TODO: This is a quick hack. Replace with goog.string.quote
|
||||
string = string.replace(/\\/g, '\\\\')
|
||||
.replace(/\n/g, '\\\n')
|
||||
.replace(/'/g, '\\\'');
|
||||
return '\'' + string + '\'';
|
||||
};
|
||||
|
||||
/**
|
||||
* Common tasks for generating Lua from blocks.
|
||||
* Handles comments for the specified block and any connected value blocks.
|
||||
* Calls any statements following this block.
|
||||
* @param {!Blockly.Block} block The current block.
|
||||
* @param {string} code The Lua code created for this block.
|
||||
* @return {string} Lua code with comments and subsequent blocks added.
|
||||
* @private
|
||||
*/
|
||||
Blockly.Lua.scrub_ = function(block, code) {
|
||||
var commentCode = '';
|
||||
// Only collect comments for blocks that aren't inline.
|
||||
if (!block.outputConnection || !block.outputConnection.targetConnection) {
|
||||
// Collect comment for this block.
|
||||
var comment = block.getCommentText();
|
||||
if (comment) {
|
||||
commentCode += Blockly.Lua.prefixLines(comment, '-- ') + '\n';
|
||||
}
|
||||
// Collect comments for all value arguments.
|
||||
// Don't collect comments for nested statements.
|
||||
for (var x = 0; x < block.inputList.length; x++) {
|
||||
if (block.inputList[x].type == Blockly.INPUT_VALUE) {
|
||||
var childBlock = block.inputList[x].connection.targetBlock();
|
||||
if (childBlock) {
|
||||
comment = Blockly.Lua.allNestedComments(childBlock);
|
||||
if (comment) {
|
||||
commentCode += Blockly.Lua.prefixLines(comment, '-- ');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var nextBlock = block.nextConnection && block.nextConnection.targetBlock();
|
||||
var nextCode = Blockly.Lua.blockToCode(nextBlock);
|
||||
return commentCode + code + nextCode;
|
||||
};
|
90
generators/lua/colour.js
Normal file
90
generators/lua/colour.js
Normal file
|
@ -0,0 +1,90 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for colour blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua.colour');
|
||||
|
||||
goog.require('Blockly.Lua');
|
||||
|
||||
|
||||
Blockly.Lua['colour_picker'] = function(block) {
|
||||
// Colour picker.
|
||||
var code = '\'' + block.getFieldValue('COLOUR') + '\'';
|
||||
return [code, Blockly.Lua.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.Lua['colour_random'] = function(block) {
|
||||
// Generate a random colour.
|
||||
var code = 'string.format("#%06x", math.random(0, 2^24 - 1))';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['colour_rgb'] = function(block) {
|
||||
// Compose a colour from RGB components expressed as percentages.
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'colour_rgb',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(r, g, b)',
|
||||
' r = math.floor(math.min(100, math.max(0, r)) * 2.55 + .5)',
|
||||
' g = math.floor(math.min(100, math.max(0, g)) * 2.55 + .5)',
|
||||
' b = math.floor(math.min(100, math.max(0, b)) * 2.55 + .5)',
|
||||
' return string.format("#%02x%02x%02x", r, g, b)',
|
||||
'end']);
|
||||
var r = Blockly.Lua.valueToCode(block, 'RED',
|
||||
Blockly.Lua.ORDER_NONE) || 0;
|
||||
var g = Blockly.Lua.valueToCode(block, 'GREEN',
|
||||
Blockly.Lua.ORDER_NONE) || 0;
|
||||
var b = Blockly.Lua.valueToCode(block, 'BLUE',
|
||||
Blockly.Lua.ORDER_NONE) || 0;
|
||||
var code = functionName + '(' + r + ', ' + g + ', ' + b + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['colour_blend'] = function(block) {
|
||||
// Blend two colours together.
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'colour_blend',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ +
|
||||
'(colour1, colour2, ratio)',
|
||||
' local r1 = tonumber(string.sub(colour1, 2, 3), 16)',
|
||||
' local r2 = tonumber(string.sub(colour2, 2, 3), 16)',
|
||||
' local g1 = tonumber(string.sub(colour1, 4, 5), 16)',
|
||||
' local g2 = tonumber(string.sub(colour2, 4, 5), 16)',
|
||||
' local b1 = tonumber(string.sub(colour1, 6, 7), 16)',
|
||||
' local b2 = tonumber(string.sub(colour2, 6, 7), 16)',
|
||||
' local ratio = math.min(1, math.max(0, ratio))',
|
||||
' local r = math.floor(r1 * (1 - ratio) + r2 * ratio + .5)',
|
||||
' local g = math.floor(g1 * (1 - ratio) + g2 * ratio + .5)',
|
||||
' local b = math.floor(b1 * (1 - ratio) + b2 * ratio + .5)',
|
||||
' return string.format("#%02x%02x%02x", r, g, b)',
|
||||
'end']);
|
||||
var colour1 = Blockly.Lua.valueToCode(block, 'COLOUR1',
|
||||
Blockly.Lua.ORDER_NONE) || '\'#000000\'';
|
||||
var colour2 = Blockly.Lua.valueToCode(block, 'COLOUR2',
|
||||
Blockly.Lua.ORDER_NONE) || '\'#000000\'';
|
||||
var ratio = Blockly.Lua.valueToCode(block, 'RATIO',
|
||||
Blockly.Lua.ORDER_NONE) || 0;
|
||||
var code = functionName + '(' + colour1 + ', ' + colour2 + ', ' + ratio + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
363
generators/lua/lists.js
Normal file
363
generators/lua/lists.js
Normal file
|
@ -0,0 +1,363 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for list blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua.lists');
|
||||
|
||||
goog.require('Blockly.Lua');
|
||||
|
||||
|
||||
Blockly.Lua['lists_create_empty'] = function(block) {
|
||||
// Create an empty list.
|
||||
// List literals must be parenthesized before indexing into.
|
||||
return ['({})', Blockly.Lua.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_create_with'] = function(block) {
|
||||
// Create a list with any number of elements of any type.
|
||||
var code = new Array(block.itemCount_);
|
||||
for (var n = 0; n < block.itemCount_; n++) {
|
||||
code[n] = Blockly.Lua.valueToCode(block, 'ADD' + n,
|
||||
Blockly.Lua.ORDER_NONE) || 'None';
|
||||
}
|
||||
code = '({' + code.join(', ') + '})';
|
||||
return [code, Blockly.Lua.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_repeat'] = function(block) {
|
||||
// Create a list with one element repeated.
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'create_list_repeated',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(item, count)',
|
||||
' local t = {}',
|
||||
' for i = 1, count do',
|
||||
' table.insert(t, item)',
|
||||
' end',
|
||||
' return t',
|
||||
'end']);
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'ITEM',
|
||||
Blockly.Lua.ORDER_NONE) || 'None';
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'NUM',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
var code = functionName + '(' + argument0 + ', ' + argument1 + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_length'] = function(block) {
|
||||
// String or array length.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_HIGH) || '({})';
|
||||
return ['#' + argument0, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_isEmpty'] = function(block) {
|
||||
// Is the string null or array empty?
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_HIGH) || '({})';
|
||||
var code = '#' + argument0 + ' == 0';
|
||||
return [code, Blockly.Lua.ORDER_RELATIONAL];
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_indexOf'] = function(block) {
|
||||
// Find an item in the list.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'FIND',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_NONE) || '({})';
|
||||
var functionName;
|
||||
if (block.getTitleValue('END') == 'FIRST') {
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'first_index',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, elem)',
|
||||
' for k, v in ipairs(t) do',
|
||||
' if v == elem then',
|
||||
' return k',
|
||||
' end',
|
||||
' end',
|
||||
' return 0',
|
||||
'end']);
|
||||
} else {
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'last_index',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, elem)',
|
||||
' for i = #t, 1, -1 do',
|
||||
' if t[i] == elem then',
|
||||
' return i',
|
||||
' end',
|
||||
' end',
|
||||
' return 0',
|
||||
'end']);
|
||||
}
|
||||
var code = functionName + '(' + argument1 + ', ' + argument0 + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns an expression calculating the index into a list.
|
||||
* @private
|
||||
* @param {string} listname Name of the list, used to calculate length.
|
||||
* @param {string} where The method of indexing, selected by dropdown in Blockly
|
||||
* @param {=string} opt_at The optional offset when indexing from start/end.
|
||||
* @return {string} Index expression.
|
||||
*/
|
||||
Blockly.Lua.lists.getIndex_ = function(listname, where, opt_at) {
|
||||
if (where == 'FIRST') {
|
||||
return 1;
|
||||
} else if (where == 'FROM_END') {
|
||||
return '#' + listname + ' + 1 - ' + opt_at;
|
||||
} else if (where == 'LAST') {
|
||||
return '#' + listname;
|
||||
} else if (where == 'RANDOM') {
|
||||
return 'math.random(#' + listname + ')';
|
||||
} else {
|
||||
return opt_at;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Counter for generating unique symbols.
|
||||
* @private
|
||||
* @type {number}
|
||||
*/
|
||||
Blockly.Lua.lists.gensym_counter_ = 0;
|
||||
|
||||
/**
|
||||
* Generate a unique symbol.
|
||||
* @private
|
||||
* @return {string} unique symbol, eg 'G123'
|
||||
*/
|
||||
Blockly.Lua.lists.gensym_ = function() {
|
||||
return 'G' + Blockly.Lua.lists.gensym_counter_++;
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_getIndex'] = function(block) {
|
||||
// Get element at index.
|
||||
// Note: Until January 2013 this block did not have MODE or WHERE inputs.
|
||||
var mode = block.getTitleValue('MODE') || 'GET';
|
||||
var where = block.getTitleValue('WHERE') || 'FROM_START';
|
||||
var at = Blockly.Lua.valueToCode(block, 'AT',
|
||||
Blockly.Lua.ORDER_ADDITIVE) || '1';
|
||||
var list = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_HIGH) || '({})';
|
||||
var getIndex_ = Blockly.Lua.lists.getIndex_;
|
||||
var gensym_ = Blockly.Lua.lists.gensym_;
|
||||
|
||||
// If `list` would be evaluated more than once (which is the case for LAST,
|
||||
// FROM_END, and RANDOM) and is non-trivial, make sure to access it only once.
|
||||
if ((where == 'LAST' || where == 'FROM_END' || where == 'RANDOM') &&
|
||||
!list.match(/^\w+$/)) {
|
||||
// `list` is an expression, so we may not evaluate it more than once.
|
||||
if (mode == 'REMOVE') {
|
||||
// We can use multiple statements.
|
||||
var listVar = Blockly.Lua.variableDB_.getDistinctName(
|
||||
'tmp_list', Blockly.Variables.NAME_TYPE);
|
||||
var code = listVar + ' = ' + list + '\n' +
|
||||
'table.remove(' + listVar + ', ' + getIndex_(listVar, where, at) +
|
||||
')\n';
|
||||
return code;
|
||||
} else {
|
||||
// We need to create a procedure to avoid reevaluating values.
|
||||
if (mode == 'GET') {
|
||||
// Note that getIndex_() ignores `at` when `where` == 'LAST' or
|
||||
// 'RANDOM', so we only need one procedure for each of those 'where'
|
||||
// values. The value for 'FROM_END' depends on `at`, so we will
|
||||
// generate a unique procedure (name) each time.
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'list_get_' + where.toLowerCase() +
|
||||
(where == 'FROM_END' ? '_' + gensym_() : ''),
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' return t[' + getIndex_('t', where, at) + ']',
|
||||
'end']);
|
||||
} else { // mode == 'GET_REMOVE'
|
||||
// We need to create a procedure.
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'list_remove_' + where.toLowerCase() +
|
||||
(where == 'FROM_END' ? '_' + gensym_() : ''),
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' return table.remove(t, ' + getIndex_('t', where, at) + ')',
|
||||
'end']);
|
||||
}
|
||||
var code = functionName + '(' + list + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
}
|
||||
} else {
|
||||
// Either `list` is a simple variable, or we only need to refer to `list`
|
||||
// once.
|
||||
if (mode == 'GET') {
|
||||
var code = list + '[' + getIndex_(list, where, at) + ']';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
} else {
|
||||
var code = 'table.remove(' + list + ', ' + getIndex_(list, where, at) +
|
||||
')';
|
||||
if (mode == 'GET_REMOVE') {
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
} else { // `mode` == 'REMOVE'
|
||||
return code + '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_setIndex'] = function(block) {
|
||||
// Set element at index.
|
||||
// Note: Until February 2013 this block did not have MODE or WHERE inputs.
|
||||
var list = Blockly.Lua.valueToCode(block, 'LIST',
|
||||
Blockly.Lua.ORDER_HIGH) || '({})';
|
||||
var mode = block.getFieldValue('MODE') || 'SET';
|
||||
var where = block.getFieldValue('WHERE') || 'FROM_START';
|
||||
var at = Blockly.Lua.valueToCode(block, 'AT',
|
||||
Blockly.Lua.ORDER_ADDITIVE) || '1';
|
||||
var value = Blockly.Lua.valueToCode(block, 'TO',
|
||||
Blockly.Lua.ORDER_NONE) || 'None';
|
||||
var getIndex_ = Blockly.Lua.lists.getIndex_;
|
||||
|
||||
// If `list` would be evaluated more than once (which is the case for LAST,
|
||||
// FROM_END, and RANDOM) and is non-trivial, make sure to access it only once.
|
||||
if ((where == 'LAST' || where == 'FROM_END' || where == 'RANDOM') &&
|
||||
!list.match(/^\w+$/)) {
|
||||
// `list` is an expression, so we may not evaluate it more than once.
|
||||
if (where == 'RANDOM' || where == 'LAST') {
|
||||
// In these cases, `at` is implicit. getIndex_() ignores its value.
|
||||
if (mode == 'SET') {
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'list_set_' + where.toLowerCase(),
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, val)',
|
||||
' t[' + getIndex_('t', where, at) + '] = val',
|
||||
'end']);
|
||||
} else { // `mode` == 'INSERT'
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'list_insert_' + where.toLowerCase(),
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t, val)',
|
||||
' table.insert(t, ' +
|
||||
// LAST is a special case, because we want to insert
|
||||
// *after* not *before*, the existing last element.
|
||||
getIndex_('t', where, at) + (where == 'LAST' ? ' + 1' : '') +
|
||||
', val)',
|
||||
'end']);
|
||||
}
|
||||
var code = functionName + '(' + list + ', ' + value + ')\n';
|
||||
return code;
|
||||
} else { // `where` = 'FROM_END'
|
||||
if (mode == 'SET') {
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'list_set_from_end',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ +
|
||||
'(t, index, val)',
|
||||
' t[#t + 1 - index] = val',
|
||||
'end']);
|
||||
} else { // `mode` == 'INSERT'
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'list_insert_from_end',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ +
|
||||
'(t, index, val)',
|
||||
' table.insert(t, #t + 1 - index, val)',
|
||||
'end']);
|
||||
}
|
||||
var code = functionName + '(' + list + ', ' + at + ', ' + value + ')\n';
|
||||
return code;
|
||||
}
|
||||
} else {
|
||||
// It's okay to have multiple references to the list.
|
||||
if (mode == 'SET') {
|
||||
var code = list + '[' + getIndex_(list, where, at) + '] = ' + value;
|
||||
} else { // `mode` == 'INSERT'
|
||||
// LAST is a special case, because we want to insert
|
||||
// *after* not *before*, the existing last element.
|
||||
var code = 'table.insert(' + list + ', ' +
|
||||
(getIndex_(list, where, at) + (where == 'LAST' ? ' + 1' : '')) +
|
||||
', ' + value + ')';
|
||||
}
|
||||
return code + '\n';
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_getSublist'] = function(block) {
|
||||
// Get sublist.
|
||||
var list = Blockly.Lua.valueToCode(block, 'LIST',
|
||||
Blockly.Lua.ORDER_HIGH) || '({})';
|
||||
var where1 = block.getFieldValue('WHERE1');
|
||||
var where2 = block.getFieldValue('WHERE2');
|
||||
var at1 = Blockly.Lua.valueToCode(block, 'AT1',
|
||||
Blockly.Lua.ORDER_ADDITIVE) || '1';
|
||||
var at2 = Blockly.Lua.valueToCode(block, 'AT2',
|
||||
Blockly.Lua.ORDER_ADDITIVE) || '1';
|
||||
var getIndex_ = Blockly.Lua.lists.getIndex_;
|
||||
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'list_sublist_' + Blockly.Lua.lists.gensym_(),
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(source)',
|
||||
' local t = {}',
|
||||
' local start = ' + getIndex_('source', where1, at1),
|
||||
' local finish = ' + getIndex_('source', where2, at2),
|
||||
' for i = start, finish do',
|
||||
' table.insert(t, source[i])',
|
||||
' end',
|
||||
' return t',
|
||||
'end']);
|
||||
var code = functionName + '(' + list + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['lists_split'] = function(block) {
|
||||
// Block for splitting text into a list, or joining a list into text.
|
||||
var value_input = Blockly.Lua.valueToCode(block, 'INPUT',
|
||||
Blockly.Lua.ORDER_NONE);
|
||||
var value_delim = Blockly.Lua.valueToCode(block, 'DELIM',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
var mode = block.getFieldValue('MODE');
|
||||
var functionName;
|
||||
if (mode == 'SPLIT') {
|
||||
if (!value_input) {
|
||||
value_input = '\'\'';
|
||||
}
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'list_string_split',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ +
|
||||
'(input, delim)',
|
||||
' local t = {}',
|
||||
' local pos = 1',
|
||||
' while true do',
|
||||
' next_delim = string.find(input, delim, pos)',
|
||||
' if next_delim == nil then',
|
||||
' table.insert(t, string.sub(input, pos))',
|
||||
' break',
|
||||
' else',
|
||||
' table.insert(t, string.sub(input, pos, next_delim-1))',
|
||||
' pos = next_delim + #delim',
|
||||
' end',
|
||||
' end',
|
||||
' return t',
|
||||
'end']);
|
||||
} else if (mode == 'JOIN') {
|
||||
if (!value_input) {
|
||||
value_input = '({})';
|
||||
}
|
||||
functionName = 'table.concat';
|
||||
} else {
|
||||
throw 'Unknown mode: ' + mode;
|
||||
}
|
||||
var code = functionName + '(' + value_input + ', ' + value_delim + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
125
generators/lua/logic.js
Normal file
125
generators/lua/logic.js
Normal file
|
@ -0,0 +1,125 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for logic blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua.logic');
|
||||
|
||||
goog.require('Blockly.Lua');
|
||||
|
||||
|
||||
Blockly.Lua['controls_if'] = function(block) {
|
||||
// If/elseif/else condition.
|
||||
var n = 0;
|
||||
var argument = Blockly.Lua.valueToCode(block, 'IF' + n,
|
||||
Blockly.Lua.ORDER_NONE) || 'false';
|
||||
var branch = Blockly.Lua.statementToCode(block, 'DO' + n);
|
||||
var code = 'if ' + argument + ' then\n' + branch;
|
||||
for (n = 1; n <= block.elseifCount_; n++) {
|
||||
argument = Blockly.Lua.valueToCode(block, 'IF' + n,
|
||||
Blockly.Lua.ORDER_NONE) || 'false';
|
||||
branch = Blockly.Lua.statementToCode(block, 'DO' + n);
|
||||
code += ' elseif ' + argument + ' then\n' + branch;
|
||||
}
|
||||
if (block.elseCount_) {
|
||||
branch = Blockly.Lua.statementToCode(block, 'ELSE');
|
||||
code += ' else\n' + branch;
|
||||
}
|
||||
return code + 'end\n';
|
||||
};
|
||||
|
||||
Blockly.Lua['logic_compare'] = function(block) {
|
||||
// Comparison operator.
|
||||
var OPERATORS = {
|
||||
'EQ': '==',
|
||||
'NEQ': '~=',
|
||||
'LT': '<',
|
||||
'LTE': '<=',
|
||||
'GT': '>',
|
||||
'GTE': '>='
|
||||
};
|
||||
var operator = OPERATORS[block.getFieldValue('OP')];
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'A',
|
||||
Blockly.Lua.ORDER_RELATIONAL) || '0';
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'B',
|
||||
Blockly.Lua.ORDER_RELATIONAL) || '0';
|
||||
var code = argument0 + ' ' + operator + ' ' + argument1;
|
||||
return [code, Blockly.Lua.ORDER_RELATIONAL];
|
||||
};
|
||||
|
||||
Blockly.Lua['logic_operation'] = function(block) {
|
||||
// Operations 'and', 'or'.
|
||||
var operator = (block.getFieldValue('OP') == 'AND') ? 'and' : 'or';
|
||||
var order = (operator == 'and') ? Blockly.Lua.ORDER_AND :
|
||||
Blockly.Lua.ORDER_OR;
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'A', order);
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'B', order);
|
||||
if (!argument0 && !argument1) {
|
||||
// If there are no arguments, then the return value is false.
|
||||
argument0 = 'false';
|
||||
argument1 = 'false';
|
||||
} else {
|
||||
// Single missing arguments have no effect on the return value.
|
||||
var defaultArgument = (operator == 'and') ? 'true' : 'false';
|
||||
if (!argument0) {
|
||||
argument0 = defaultArgument;
|
||||
}
|
||||
if (!argument1) {
|
||||
argument1 = defaultArgument;
|
||||
}
|
||||
}
|
||||
var code = argument0 + ' ' + operator + ' ' + argument1;
|
||||
return [code, order];
|
||||
};
|
||||
|
||||
Blockly.Lua['logic_negate'] = function(block) {
|
||||
// Negation.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'BOOL',
|
||||
Blockly.Lua.ORDER_UNARY) || 'true';
|
||||
var code = 'not ' + argument0;
|
||||
return [code, Blockly.Lua.ORDER_UNARY];
|
||||
};
|
||||
|
||||
Blockly.Lua['logic_boolean'] = function(block) {
|
||||
// Boolean values true and false.
|
||||
var code = (block.getFieldValue('BOOL') == 'TRUE') ? 'true' : 'false';
|
||||
return [code, Blockly.Lua.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.Lua['logic_null'] = function(block) {
|
||||
// Null data type.
|
||||
return ['nil', Blockly.Lua.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.Lua['logic_ternary'] = function(block) {
|
||||
// Ternary operator.
|
||||
var value_if = Blockly.Lua.valueToCode(block, 'IF',
|
||||
Blockly.Lua.ORDER_AND) || 'false';
|
||||
var value_then = Blockly.Lua.valueToCode(block, 'THEN',
|
||||
Blockly.Lua.ORDER_AND) || 'nil';
|
||||
var value_else = Blockly.Lua.valueToCode(block, 'ELSE',
|
||||
Blockly.Lua.ORDER_OR) || 'nil';
|
||||
var code = value_if + ' and ' + value_then + ' or ' + value_else;
|
||||
return [code, Blockly.Lua.ORDER_OR];
|
||||
};
|
166
generators/lua/loops.js
Normal file
166
generators/lua/loops.js
Normal file
|
@ -0,0 +1,166 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for loop blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua.loops');
|
||||
|
||||
goog.require('Blockly.Lua');
|
||||
|
||||
|
||||
/**
|
||||
* This is the text used to implement a <pre>continue</pre>.
|
||||
* It is also used to recognise <pre>continue</pre>s in generated code so that
|
||||
* the appropriate label can be put at the end of the loop body.
|
||||
* @const {string}
|
||||
*/
|
||||
Blockly.Lua.CONTINUE_STATEMENT = 'goto continue\n';
|
||||
|
||||
/**
|
||||
* If the loop body contains a "goto continue" statement, add a continue label
|
||||
* to the loop body. Slightly inefficient, as continue labels will be generated
|
||||
* in all outer loops, but this is safer than duplicating the logic of
|
||||
* blockToCode.
|
||||
*
|
||||
* @param {string} branch Generated code of the loop body
|
||||
* @return {string} Generated label or '' if unnecessary
|
||||
*/
|
||||
Blockly.Lua.addContinueLabel = function(branch) {
|
||||
if (branch.indexOf(Blockly.Lua.CONTINUE_STATEMENT) > -1) {
|
||||
return branch + Blockly.Lua.INDENT + '::continue::\n';
|
||||
} else {
|
||||
return branch;
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Lua['controls_repeat'] = function(block) {
|
||||
// Repeat n times (internal number).
|
||||
var repeats = parseInt(block.getFieldValue('TIMES'), 10);
|
||||
var branch = Blockly.Lua.statementToCode(block, 'DO') || '';
|
||||
branch = Blockly.Lua.addContinueLabel(branch);
|
||||
var loopVar = Blockly.Lua.variableDB_.getDistinctName(
|
||||
'count', Blockly.Variables.NAME_TYPE);
|
||||
var code = 'for ' + loopVar + ' = 1, ' + repeats + ' do\n' + branch + 'end\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
Blockly.Lua['controls_repeat_ext'] = function(block) {
|
||||
// Repeat n times (external number).
|
||||
var repeats = Blockly.Lua.valueToCode(block, 'TIMES',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
if (Blockly.isNumber(repeats)) {
|
||||
repeats = parseInt(repeats, 10);
|
||||
} else {
|
||||
repeats = 'math.floor(' + repeats + ')';
|
||||
}
|
||||
var branch = Blockly.Lua.statementToCode(block, 'DO') || '\n';
|
||||
branch = Blockly.Lua.addContinueLabel(branch);
|
||||
var loopVar = Blockly.Lua.variableDB_.getDistinctName(
|
||||
'count', Blockly.Variables.NAME_TYPE);
|
||||
var code = 'for ' + loopVar + ' = 1, ' + repeats + ' do\n' +
|
||||
branch + 'end\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
Blockly.Lua['controls_whileUntil'] = function(block) {
|
||||
// Do while/until loop.
|
||||
var until = block.getFieldValue('MODE') == 'UNTIL';
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'BOOL',
|
||||
until ? Blockly.Lua.ORDER_UNARY :
|
||||
Blockly.Lua.ORDER_NONE) || 'false';
|
||||
var branch = Blockly.Lua.statementToCode(block, 'DO') || '\n';
|
||||
branch = Blockly.Lua.addLoopTrap(branch, block.id);
|
||||
branch = Blockly.Lua.addContinueLabel(branch);
|
||||
if (until) {
|
||||
argument0 = 'not ' + argument0;
|
||||
}
|
||||
return 'while ' + argument0 + ' do\n' + branch + 'end\n';
|
||||
};
|
||||
|
||||
Blockly.Lua['controls_for'] = function(block) {
|
||||
// For loop.
|
||||
var variable0 = Blockly.Lua.variableDB_.getName(
|
||||
block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
|
||||
var startVar = Blockly.Lua.valueToCode(block, 'FROM',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
var endVar = Blockly.Lua.valueToCode(block, 'TO',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
var increment = Blockly.Lua.valueToCode(block, 'BY',
|
||||
Blockly.Lua.ORDER_NONE) || '1';
|
||||
var branch = Blockly.Lua.statementToCode(block, 'DO') || '\n';
|
||||
branch = Blockly.Lua.addLoopTrap(branch, block.id);
|
||||
branch = Blockly.Lua.addContinueLabel(branch);
|
||||
var code = '';
|
||||
var incValue;
|
||||
if (Blockly.isNumber(startVar) && Blockly.isNumber(endVar) &&
|
||||
Blockly.isNumber(increment)) {
|
||||
// All arguments are simple numbers.
|
||||
var up = parseFloat(startVar) <= parseFloat(endVar);
|
||||
var step = Math.abs(parseFloat(increment));
|
||||
incValue = (up ? '' : '-') + step;
|
||||
} else {
|
||||
code = '';
|
||||
// Determine loop direction at start, in case one of the bounds
|
||||
// changes during loop execution.
|
||||
incValue = Blockly.Lua.variableDB_.getDistinctName(
|
||||
variable0 + '_inc', Blockly.Variables.NAME_TYPE);
|
||||
code += incValue + ' = ';
|
||||
if (Blockly.isNumber(increment)) {
|
||||
code += Math.abs(increment) + '\n';
|
||||
} else {
|
||||
code += 'math.abs(' + increment + ')\n';
|
||||
}
|
||||
code += 'if (' + startVar + ') > (' + endVar + ') then\n';
|
||||
code += Blockly.Lua.INDENT + incValue + ' = -' + incValue + '\n';
|
||||
code += 'end\n';
|
||||
}
|
||||
code += 'for ' + variable0 + ' = ' + startVar + ', ' + endVar +
|
||||
', ' + incValue;
|
||||
code += ' do\n' + branch + 'end\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
Blockly.Lua['controls_forEach'] = function(block) {
|
||||
// For each loop.
|
||||
var variable0 = Blockly.Lua.variableDB_.getName(
|
||||
block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'LIST',
|
||||
Blockly.Lua.ORDER_NONE) || '{}';
|
||||
var branch = Blockly.Lua.statementToCode(block, 'DO') || '\n';
|
||||
branch = Blockly.Lua.addContinueLabel(branch);
|
||||
var code = 'for _, ' + variable0 + ' in ipairs(' + argument0 + ') do \n' +
|
||||
branch + 'end\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
Blockly.Lua['controls_flow_statements'] = function(block) {
|
||||
// Flow statements: continue, break.
|
||||
switch (block.getFieldValue('FLOW')) {
|
||||
case 'BREAK':
|
||||
return 'break\n';
|
||||
case 'CONTINUE':
|
||||
return Blockly.Lua.CONTINUE_STATEMENT;
|
||||
}
|
||||
throw 'Unknown flow statement.';
|
||||
};
|
425
generators/lua/math.js
Normal file
425
generators/lua/math.js
Normal file
|
@ -0,0 +1,425 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for math blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua.math');
|
||||
|
||||
goog.require('Blockly.Lua');
|
||||
|
||||
|
||||
Blockly.Lua['math_number'] = function(block) {
|
||||
// Numeric value.
|
||||
var code = parseFloat(block.getFieldValue('NUM'));
|
||||
var order = code < 0 ? Blockly.Lua.ORDER_UNARY :
|
||||
Blockly.Lua.ORDER_ATOMIC;
|
||||
return [code, order];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_arithmetic'] = function(block) {
|
||||
// Basic arithmetic operators, and power.
|
||||
var OPERATORS = {
|
||||
ADD: [' + ', Blockly.Lua.ORDER_ADDITIVE],
|
||||
MINUS: [' - ', Blockly.Lua.ORDER_ADDITIVE],
|
||||
MULTIPLY: [' * ', Blockly.Lua.ORDER_MULTIPLICATIVE],
|
||||
DIVIDE: [' / ', Blockly.Lua.ORDER_MULTIPLICATIVE],
|
||||
POWER: [' ^ ', Blockly.Lua.ORDER_EXPONENTIATION]
|
||||
};
|
||||
var tuple = OPERATORS[block.getFieldValue('OP')];
|
||||
var operator = tuple[0];
|
||||
var order = tuple[1];
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'A', order) || '0';
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'B', order) || '0';
|
||||
var code = argument0 + operator + argument1;
|
||||
return [code, order];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_single'] = function(block) {
|
||||
// Math operators with single operand.
|
||||
var operator = block.getFieldValue('OP');
|
||||
var code;
|
||||
var arg;
|
||||
if (operator == 'NEG') {
|
||||
// Negation is a special case given its different operator precedence.
|
||||
arg = Blockly.Lua.valueToCode(block, 'NUM',
|
||||
Blockly.Lua.ORDER_UNARY) || '0';
|
||||
return ['-' + arg, Blockly.Lua.ORDER_UNARY];
|
||||
}
|
||||
if (operator == 'SIN' || operator == 'COS' || operator == 'TAN') {
|
||||
arg = Blockly.Lua.valueToCode(block, 'NUM',
|
||||
Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
|
||||
} else {
|
||||
arg = Blockly.Lua.valueToCode(block, 'NUM',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
}
|
||||
switch (operator) {
|
||||
case 'ABS':
|
||||
code = 'math.abs(' + arg + ')';
|
||||
break;
|
||||
case 'ROOT':
|
||||
code = 'math.sqrt(' + arg + ')';
|
||||
break;
|
||||
case 'LN':
|
||||
code = 'math.log(' + arg + ')';
|
||||
break;
|
||||
case 'LOG10':
|
||||
code = 'math.log10(' + arg + ')';
|
||||
break;
|
||||
case 'EXP':
|
||||
code = 'math.exp(' + arg + ')';
|
||||
break;
|
||||
case 'POW10':
|
||||
code = 'math.pow(10,' + arg + ')';
|
||||
break;
|
||||
case 'ROUND':
|
||||
// This rounds up. Blockly does not specify rounding direction.
|
||||
code = 'math.floor(' + arg + ' + .5)';
|
||||
break;
|
||||
case 'ROUNDUP':
|
||||
code = 'math.ceil(' + arg + ')';
|
||||
break;
|
||||
case 'ROUNDDOWN':
|
||||
code = 'math.floor(' + arg + ')';
|
||||
break;
|
||||
case 'SIN':
|
||||
code = 'math.sin(math.rad(' + arg + '))';
|
||||
break;
|
||||
case 'COS':
|
||||
code = 'math.cos(math.rad(' + arg + '))';
|
||||
break;
|
||||
case 'TAN':
|
||||
code = 'math.tan(math.rad(' + arg + '))';
|
||||
break;
|
||||
case 'ASIN':
|
||||
code = 'math.deg(math.asin(' + arg + '))';
|
||||
break;
|
||||
case 'ACOS':
|
||||
code = 'math.deg(math.acos(' + arg + '))';
|
||||
break;
|
||||
case 'ATAN':
|
||||
code = 'math.deg(math.atan(' + arg + '))';
|
||||
break;
|
||||
default:
|
||||
throw 'Unknown math operator: ' + operator;
|
||||
}
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_constant'] = function(block) {
|
||||
// Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
|
||||
var CONSTANTS = {
|
||||
PI: ['math.pi', Blockly.Lua.ORDER_HIGH],
|
||||
E: ['math.exp(1)', Blockly.Lua.ORDER_HIGH],
|
||||
GOLDEN_RATIO: ['(1 + math.sqrt(5)) / 2', Blockly.Lua.ORDER_MULTIPLICATIVE],
|
||||
SQRT2: ['math.sqrt(2)', Blockly.Lua.ORDER_HIGH],
|
||||
SQRT1_2: ['math.sqrt(1 / 2)', Blockly.Lua.ORDER_HIGH],
|
||||
INFINITY: ['math.huge', Blockly.Lua.ORDER_HIGH]
|
||||
};
|
||||
return CONSTANTS[block.getFieldValue('CONSTANT')];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_number_property'] = function(block) {
|
||||
// Check if a number is even, odd, prime, whole, positive, or negative
|
||||
// or if it is divisible by certain number. Returns true or false.
|
||||
var number_to_check = Blockly.Lua.valueToCode(block, 'NUMBER_TO_CHECK',
|
||||
Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
|
||||
var dropdown_property = block.getFieldValue('PROPERTY');
|
||||
var code;
|
||||
if (dropdown_property == 'PRIME') {
|
||||
// Prime is a special case as it is not a one-liner test.
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'math_isPrime',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(n)',
|
||||
' -- https://en.wikipedia.org/wiki/Primality_test#Naive_methods',
|
||||
' if n == 2 or n == 3 then',
|
||||
' return true',
|
||||
' end',
|
||||
' -- False if n is NaN, negative, is 1, or not whole.',
|
||||
' -- And false if n is divisible by 2 or 3.',
|
||||
' if not(n > 1) or n % 1 ~= 0 or n % 2 == 0 or n % 3 == 0 then',
|
||||
' return false',
|
||||
' end',
|
||||
' -- Check all the numbers of form 6k +/- 1, up to sqrt(n).',
|
||||
' for x = 6, math.sqrt(n) + 1.5, 6 do',
|
||||
' if n % (x - 1) == 0 or n % (x + 1) == 0 then',
|
||||
' return false',
|
||||
' end',
|
||||
' end',
|
||||
' return true',
|
||||
'end']);
|
||||
code = functionName + '(' + number_to_check + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
}
|
||||
switch (dropdown_property) {
|
||||
case 'EVEN':
|
||||
code = number_to_check + ' % 2 == 0';
|
||||
break;
|
||||
case 'ODD':
|
||||
code = number_to_check + ' % 2 == 1';
|
||||
break;
|
||||
case 'WHOLE':
|
||||
code = number_to_check + ' % 1 == 0';
|
||||
break;
|
||||
case 'POSITIVE':
|
||||
code = number_to_check + ' > 0';
|
||||
break;
|
||||
case 'NEGATIVE':
|
||||
code = number_to_check + ' < 0';
|
||||
break;
|
||||
case 'DIVISIBLE_BY':
|
||||
var divisor = Blockly.Lua.valueToCode(block, 'DIVISOR',
|
||||
Blockly.Lua.ORDER_MULTIPLICATIVE);
|
||||
// If 'divisor' is some code that evals to 0, Lua will produce a nan.
|
||||
// Let's produce nil if we can determine this at compile-time.
|
||||
if (!divisor || divisor == '0') {
|
||||
return ['nil', Blockly.Lua.ORDER_ATOMIC];
|
||||
}
|
||||
// The normal trick to implement ?: with and/or doesn't work here:
|
||||
// divisor == 0 and nil or number_to_check % divisor == 0
|
||||
// because nil is false, so allow a runtime failure. :-(
|
||||
code = number_to_check + ' % ' + divisor + ' == 0';
|
||||
break;
|
||||
}
|
||||
return [code, Blockly.Lua.ORDER_RELATIONAL];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_change'] = function(block) {
|
||||
// Add to a variable in place.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'DELTA',
|
||||
Blockly.Lua.ORDER_ADDITIVE) || '0';
|
||||
var varName = Blockly.Lua.variableDB_.getName(
|
||||
block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
|
||||
return varName + ' = ' + varName + ' + ' + argument0 + '\n';
|
||||
};
|
||||
|
||||
// Rounding functions have a single operand.
|
||||
Blockly.Lua['math_round'] = Blockly.Lua['math_single'];
|
||||
// Trigonometry functions have a single operand.
|
||||
Blockly.Lua['math_trig'] = Blockly.Lua['math_single'];
|
||||
|
||||
Blockly.Lua['math_on_list'] = function(block) {
|
||||
// Math functions for lists.
|
||||
var func = block.getFieldValue('OP');
|
||||
var list = Blockly.Lua.valueToCode(block, 'LIST',
|
||||
Blockly.Lua.ORDER_NONE) || '{}';
|
||||
var functionName;
|
||||
|
||||
// Functions needed in more than one case.
|
||||
function provideSum() {
|
||||
return Blockly.Lua.provideFunction_(
|
||||
'math_sum',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' local result = 0',
|
||||
' for _, v in ipairs(t) do',
|
||||
' result = result + v',
|
||||
' end',
|
||||
' return result',
|
||||
'end']);
|
||||
}
|
||||
|
||||
switch (func) {
|
||||
case 'SUM':
|
||||
functionName = provideSum();
|
||||
break;
|
||||
|
||||
case 'MIN':
|
||||
// Returns 0 for the empty list.
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'math_min',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' if #t == 0 then',
|
||||
' return 0',
|
||||
' end',
|
||||
' local result = math.huge',
|
||||
' for _, v in ipairs(t) do',
|
||||
' if v < result then',
|
||||
' result = v',
|
||||
' end',
|
||||
' end',
|
||||
' return result',
|
||||
'end']);
|
||||
break;
|
||||
|
||||
case 'AVERAGE':
|
||||
// Returns 0 for the empty list.
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'math_average',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' if #t == 0 then',
|
||||
' return 0',
|
||||
' end',
|
||||
' return ' + provideSum() + '(t) / #t',
|
||||
'end']);
|
||||
break;
|
||||
|
||||
case 'MAX':
|
||||
// Returns 0 for the empty list.
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'math_max',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' if #t == 0 then',
|
||||
' return 0',
|
||||
' end',
|
||||
' local result = -math.huge',
|
||||
' for _, v in ipairs(t) do',
|
||||
' if v > result then',
|
||||
' result = v',
|
||||
' end',
|
||||
' end',
|
||||
' return result',
|
||||
'end']);
|
||||
break;
|
||||
|
||||
case 'MEDIAN':
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'math_median',
|
||||
// This operation excludes non-numbers.
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' -- Source: http://lua-users.org/wiki/SimpleStats',
|
||||
' if #t == 0 then',
|
||||
' return 0',
|
||||
' end',
|
||||
' local temp={}',
|
||||
' for _, v in ipairs(t) do',
|
||||
' if type(v) == "number" then',
|
||||
' table.insert(temp, v)',
|
||||
' end',
|
||||
' end',
|
||||
' table.sort(temp)',
|
||||
' if #temp % 2 == 0 then',
|
||||
' return (temp[#temp/2] + temp[(#temp/2)+1]) / 2',
|
||||
' else',
|
||||
' return temp[math.ceil(#temp/2)]',
|
||||
' end',
|
||||
'end']);
|
||||
break;
|
||||
|
||||
case 'MODE':
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'math_modes',
|
||||
// As a list of numbers can contain more than one mode,
|
||||
// the returned result is provided as an array.
|
||||
// The Lua version includes non-numbers.
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' -- Source: http://lua-users.org/wiki/SimpleStats',
|
||||
' local counts={}',
|
||||
' for _, v in ipairs(t) do',
|
||||
' if counts[v] == nil then',
|
||||
' counts[v] = 1',
|
||||
' else',
|
||||
' counts[v] = counts[v] + 1',
|
||||
' end',
|
||||
' end',
|
||||
' local biggestCount = 0',
|
||||
' for _, v in pairs(counts) do',
|
||||
' if v > biggestCount then',
|
||||
' biggestCount = v',
|
||||
' end',
|
||||
' end',
|
||||
' local temp={}',
|
||||
' for k, v in pairs(counts) do',
|
||||
' if v == biggestCount then',
|
||||
' table.insert(temp, k)',
|
||||
' end',
|
||||
' end',
|
||||
' return temp',
|
||||
'end']);
|
||||
break;
|
||||
|
||||
case 'STD_DEV':
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'math_standard_deviation',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' local m',
|
||||
' local vm',
|
||||
' local total = 0',
|
||||
' local count = 0',
|
||||
' local result',
|
||||
' m = #t == 0 and 0 or ' + provideSum() + '(t) / #t',
|
||||
' for _, v in ipairs(t) do',
|
||||
" if type(v) == 'number' then",
|
||||
' vm = v - m',
|
||||
' total = total + (vm * vm)',
|
||||
' count = count + 1',
|
||||
' end',
|
||||
' end',
|
||||
' result = math.sqrt(total / (count-1))',
|
||||
' return result',
|
||||
'end']);
|
||||
break;
|
||||
|
||||
case 'RANDOM':
|
||||
functionName = Blockly.Lua.provideFunction_(
|
||||
'math_random_list',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(t)',
|
||||
' if #t == 0 then',
|
||||
' return nil',
|
||||
' end',
|
||||
' return t[math.random(#t)]',
|
||||
'end']);
|
||||
break;
|
||||
|
||||
default:
|
||||
throw 'Unknown operator: ' + func;
|
||||
}
|
||||
return [functionName + '(' + list + ')', Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_modulo'] = function(block) {
|
||||
// Remainder computation.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'DIVIDEND',
|
||||
Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'DIVISOR',
|
||||
Blockly.Lua.ORDER_MULTIPLICATIVE) || '0';
|
||||
var code = argument0 + ' % ' + argument1;
|
||||
return [code, Blockly.Lua.ORDER_MULTIPLICATIVE];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_constrain'] = function(block) {
|
||||
// Constrain a number between two limits.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'LOW',
|
||||
Blockly.Lua.ORDER_NONE) || '-math.huge';
|
||||
var argument2 = Blockly.Lua.valueToCode(block, 'HIGH',
|
||||
Blockly.Lua.ORDER_NONE) || 'math.huge';
|
||||
var code = 'math.min(math.max(' + argument0 + ', ' + argument1 + '), ' +
|
||||
argument2 + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_random_int'] = function(block) {
|
||||
// Random integer between [X] and [Y].
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'FROM',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'TO',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
var code = 'math.random(' + argument0 + ', ' + argument1 + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['math_random_float'] = function(block) {
|
||||
// Random fraction between 0 and 1.
|
||||
return ['math.random()', Blockly.Lua.ORDER_HIGH];
|
||||
};
|
110
generators/lua/procedures.js
Normal file
110
generators/lua/procedures.js
Normal file
|
@ -0,0 +1,110 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for procedure blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua.procedures');
|
||||
|
||||
goog.require('Blockly.Lua');
|
||||
|
||||
|
||||
Blockly.Lua['procedures_defreturn'] = function(block) {
|
||||
// Define a procedure with a return value.
|
||||
var funcName = Blockly.Lua.variableDB_.getName(
|
||||
block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE);
|
||||
var branch = Blockly.Lua.statementToCode(block, 'STACK');
|
||||
if (Blockly.Lua.STATEMENT_PREFIX) {
|
||||
branch = Blockly.Lua.prefixLines(
|
||||
Blockly.Lua.STATEMENT_PREFIX.replace(/%1/g,
|
||||
'\'' + block.id + '\''), Blockly.Lua.INDENT) + branch;
|
||||
}
|
||||
if (Blockly.Lua.INFINITE_LOOP_TRAP) {
|
||||
branch = Blockly.Lua.INFINITE_LOOP_TRAP.replace(/%1/g,
|
||||
'\'' + block.id + '\'') + branch;
|
||||
}
|
||||
var returnValue = Blockly.Lua.valueToCode(block, 'RETURN',
|
||||
Blockly.Lua.ORDER_NONE) || '';
|
||||
if (returnValue) {
|
||||
returnValue = ' return ' + returnValue + '\n';
|
||||
} else if (!branch) {
|
||||
branch = '';
|
||||
}
|
||||
var args = [];
|
||||
for (var x = 0; x < block.arguments_.length; x++) {
|
||||
args[x] = Blockly.Lua.variableDB_.getName(block.arguments_[x],
|
||||
Blockly.Variables.NAME_TYPE);
|
||||
}
|
||||
var code = 'function ' + funcName + '(' + args.join(', ') + ')\n' +
|
||||
branch + returnValue + 'end\n';
|
||||
code = Blockly.Lua.scrub_(block, code);
|
||||
Blockly.Lua.definitions_[funcName] = code;
|
||||
return null;
|
||||
};
|
||||
|
||||
// Defining a procedure without a return value uses the same generator as
|
||||
// a procedure with a return value.
|
||||
Blockly.Lua['procedures_defnoreturn'] =
|
||||
Blockly.Lua['procedures_defreturn'];
|
||||
|
||||
Blockly.Lua['procedures_callreturn'] = function(block) {
|
||||
// Call a procedure with a return value.
|
||||
var funcName = Blockly.Lua.variableDB_.getName(
|
||||
block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE);
|
||||
var args = [];
|
||||
for (var x = 0; x < block.arguments_.length; x++) {
|
||||
args[x] = Blockly.Lua.valueToCode(block, 'ARG' + x,
|
||||
Blockly.Lua.ORDER_NONE) || 'nil';
|
||||
}
|
||||
var code = funcName + '(' + args.join(', ') + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['procedures_callnoreturn'] = function(block) {
|
||||
// Call a procedure with no return value.
|
||||
var funcName = Blockly.Lua.variableDB_.getName(
|
||||
block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE);
|
||||
var args = [];
|
||||
for (var x = 0; x < block.arguments_.length; x++) {
|
||||
args[x] = Blockly.Lua.valueToCode(block, 'ARG' + x,
|
||||
Blockly.Lua.ORDER_NONE) || 'nil';
|
||||
}
|
||||
var code = funcName + '(' + args.join(', ') + ')\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
Blockly.Lua['procedures_ifreturn'] = function(block) {
|
||||
// Conditionally return value from a procedure.
|
||||
var condition = Blockly.Lua.valueToCode(block, 'CONDITION',
|
||||
Blockly.Lua.ORDER_NONE) || 'false';
|
||||
var code = 'if ' + condition + ' then\n';
|
||||
if (block.hasReturnValue_) {
|
||||
var value = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_NONE) || 'nil';
|
||||
code += ' return ' + value + '\n';
|
||||
} else {
|
||||
code += ' return\n';
|
||||
}
|
||||
code += 'end\n';
|
||||
return code;
|
||||
};
|
293
generators/lua/text.js
Normal file
293
generators/lua/text.js
Normal file
|
@ -0,0 +1,293 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for text blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua.texts');
|
||||
|
||||
goog.require('Blockly.Lua');
|
||||
|
||||
|
||||
Blockly.Lua['text'] = function(block) {
|
||||
// Text value.
|
||||
var code = Blockly.Lua.quote_(block.getFieldValue('TEXT'));
|
||||
return [code, Blockly.Lua.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_join'] = function(block) {
|
||||
// Create a string made up of any number of elements of any type.
|
||||
if (block.itemCount_ == 0) {
|
||||
return ['\'\'', Blockly.Lua.ORDER_ATOMIC];
|
||||
} else if (block.itemCount_ == 1) {
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'ADD0',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
var code = argument0;
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
} else if (block.itemCount_ == 2) {
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'ADD0',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
var argument1 = Blockly.Lua.valueToCode(block, 'ADD1',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
var code = argument0 + ' .. ' + argument1;
|
||||
return [code, Blockly.Lua.ORDER_UNARY];
|
||||
} else {
|
||||
var code = [];
|
||||
for (var n = 0; n < block.itemCount_; n++) {
|
||||
code[n] = Blockly.Lua.valueToCode(block, 'ADD' + n,
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
}
|
||||
code = 'table.concat({' + code.join(', ') + '})';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
}
|
||||
};
|
||||
|
||||
Blockly.Lua['text_append'] = function(block) {
|
||||
// Append to a variable in place.
|
||||
var varName = Blockly.Lua.variableDB_.getName(
|
||||
block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'TEXT',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
return varName + ' = ' + varName + ' .. ' + argument0 + '\n';
|
||||
};
|
||||
|
||||
Blockly.Lua['text_length'] = function(block) {
|
||||
// String or array length.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_HIGH) || '\'\'';
|
||||
return ['#' + argument0, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_isEmpty'] = function(block) {
|
||||
// Is the string null or array empty?
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_HIGH) || '\'\'';
|
||||
return ['#' + argument0 + ' == 0', Blockly.Lua.ORDER_RELATIONAL];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_indexOf'] = function(block) {
|
||||
// Search the text for a substring.
|
||||
var operator = block.getFieldValue('END') == 'FIRST' ?
|
||||
'indexOf' : 'lastIndexOf';
|
||||
var substr = Blockly.Lua.valueToCode(block, 'FIND',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
var str = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
if (block.getTitleValue('END') == 'FIRST') {
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'firstIndexOf',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ +
|
||||
'(str, substr) ',
|
||||
' local i = string.find(str, substr, 1, true)',
|
||||
' if i == nil then',
|
||||
' return 0',
|
||||
' else',
|
||||
' return i',
|
||||
' end',
|
||||
'end']);
|
||||
} else {
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'lastIndexOf',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ +
|
||||
'(str, substr)',
|
||||
' local i = string.find(string.reverse(str), ' +
|
||||
'string.reverse(substr), 1, true)',
|
||||
' if i then',
|
||||
' return #str + 2 - i - #substr',
|
||||
' end',
|
||||
' return 0',
|
||||
'end']);
|
||||
}
|
||||
var code = functionName + '(' + str + ', ' + substr + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_charAt'] = function(block) {
|
||||
// Get letter at index.
|
||||
// Note: Until January 2013 this block did not have the WHERE input.
|
||||
var where = block.getFieldValue('WHERE') || 'FROM_START';
|
||||
var at = Blockly.Lua.valueToCode(block, 'AT',
|
||||
Blockly.Lua.ORDER_UNARY) || '1';
|
||||
var text = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
var code;
|
||||
if (where == 'RANDOM') {
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'text_random_letter',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(str)',
|
||||
' local index = math.random(string.len(str))',
|
||||
' return string.sub(str, index, index)',
|
||||
'end']);
|
||||
code = functionName + '(' + text + ')';
|
||||
} else {
|
||||
if (where == 'FIRST') {
|
||||
var start = '1';
|
||||
} else if (where == 'LAST') {
|
||||
var start = '-1';
|
||||
} else {
|
||||
if (where == 'FROM_START') {
|
||||
var start = at;
|
||||
} else if (where == 'FROM_END') {
|
||||
var start = '-' + at;
|
||||
} else {
|
||||
throw 'Unhandled option (text_charAt).';
|
||||
}
|
||||
}
|
||||
if (start.match(/^-?\w*$/)) {
|
||||
code = 'string.sub(' + text + ', ' + start + ', ' + start + ')';
|
||||
} else {
|
||||
// use function to avoid reevaluation
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'text_char_at',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ +
|
||||
'(str, index)',
|
||||
' return string.sub(str, index, index)',
|
||||
'end']);
|
||||
code = functionName + '(' + text + ', ' + start + ')';
|
||||
}
|
||||
}
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_getSubstring'] = function(block) {
|
||||
// Get substring.
|
||||
var text = Blockly.Lua.valueToCode(block, 'STRING',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
|
||||
// Get start index.
|
||||
var where1 = block.getFieldValue('WHERE1');
|
||||
var at1 = Blockly.Lua.valueToCode(block, 'AT1',
|
||||
Blockly.Lua.ORDER_UNARY) || '1';
|
||||
if (where1 == 'FIRST') {
|
||||
var start = 1;
|
||||
} else if (where1 == 'FROM_START') {
|
||||
var start = at1;
|
||||
} else if (where1 == 'FROM_END') {
|
||||
var start = '-' + at1;
|
||||
} else {
|
||||
throw 'Unhandled option (text_getSubstring)';
|
||||
}
|
||||
|
||||
// Get end index.
|
||||
var where2 = block.getFieldValue('WHERE2');
|
||||
var at2 = Blockly.Lua.valueToCode(block, 'AT2',
|
||||
Blockly.Lua.ORDER_UNARY) || '1';
|
||||
if (where2 == 'LAST') {
|
||||
var end = -1;
|
||||
} else if (where2 == 'FROM_START') {
|
||||
var end = at2;
|
||||
} else if (where2 == 'FROM_END') {
|
||||
var end = '-' + at2;
|
||||
} else {
|
||||
throw 'Unhandled option (text_getSubstring)';
|
||||
}
|
||||
var code = 'string.sub(' + text + ', ' + start + ', ' + end + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_changeCase'] = function(block) {
|
||||
// Change capitalization.
|
||||
var operator = block.getFieldValue('CASE');
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'TEXT',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
if (operator == 'UPPERCASE') {
|
||||
var functionName = 'string.upper';
|
||||
} else if (operator == 'LOWERCASE') {
|
||||
var functionName = 'string.lower';
|
||||
} else if (operator == 'TITLECASE') {
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'text_titlecase',
|
||||
// There are shorter versions at
|
||||
// http://lua-users.org/wiki/SciteTitleCase
|
||||
// that do not preserve whitespace.
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(str)',
|
||||
' local buf = {}',
|
||||
' local inWord = false',
|
||||
' for i = 1, #str do',
|
||||
' local c = string.sub(str, i, i)',
|
||||
' if inWord then',
|
||||
' table.insert(buf, string.lower(c))',
|
||||
' if string.find(c, "%s") then',
|
||||
' inWord = false',
|
||||
' end',
|
||||
' else',
|
||||
' table.insert(buf, string.upper(c))',
|
||||
' inWord = true',
|
||||
' end',
|
||||
' end',
|
||||
' return table.concat(buf)',
|
||||
'end']);
|
||||
}
|
||||
var code = functionName + '(' + argument0 + ')';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_trim'] = function(block) {
|
||||
// Trim spaces.
|
||||
var OPERATORS = {
|
||||
LEFT: '^%s*(,-)',
|
||||
RIGHT: '(.-)%s*$',
|
||||
BOTH: '^%s*(.-)%s*$'
|
||||
};
|
||||
var operator = OPERATORS[block.getFieldValue('MODE')];
|
||||
var text = Blockly.Lua.valueToCode(block, 'TEXT',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
var code = 'string.gsub(' + text + ', "' + operator + '", "%1")';
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_print'] = function(block) {
|
||||
// Print statement.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'TEXT',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
return 'print(' + argument0 + ')\n';
|
||||
};
|
||||
|
||||
Blockly.Lua['text_prompt_ext'] = function(block) {
|
||||
// Prompt function.
|
||||
if (block.getField('TEXT')) {
|
||||
// Internal message.
|
||||
var msg = Blockly.Lua.quote_(block.getFieldValue('TEXT'));
|
||||
} else {
|
||||
// External message.
|
||||
var msg = Blockly.Lua.valueToCode(block, 'TEXT',
|
||||
Blockly.Lua.ORDER_NONE) || '\'\'';
|
||||
}
|
||||
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'text_prompt',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(msg)',
|
||||
' io.write(msg)',
|
||||
' io.flush()',
|
||||
' return io.read()',
|
||||
'end']);
|
||||
var code = functionName + '(' + msg + ')';
|
||||
|
||||
var toNumber = block.getFieldValue('TYPE') == 'NUMBER';
|
||||
if (toNumber) {
|
||||
code = 'tonumber(' + code + ', 10)';
|
||||
}
|
||||
return [code, Blockly.Lua.ORDER_HIGH];
|
||||
};
|
||||
|
||||
Blockly.Lua['text_prompt'] = Blockly.Lua['text_prompt_ext'];
|
46
generators/lua/variables.js
Normal file
46
generators/lua/variables.js
Normal file
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for variable blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
goog.provide('Blockly.Lua.variables');
|
||||
|
||||
goog.require('Blockly.Lua');
|
||||
|
||||
|
||||
Blockly.Lua['variables_get'] = function(block) {
|
||||
// Variable getter.
|
||||
var code = Blockly.Lua.variableDB_.getName(block.getFieldValue('VAR'),
|
||||
Blockly.Variables.NAME_TYPE);
|
||||
return [code, Blockly.Lua.ORDER_ATOMIC];
|
||||
};
|
||||
|
||||
Blockly.Lua['variables_set'] = function(block) {
|
||||
// Variable setter.
|
||||
var argument0 = Blockly.Lua.valueToCode(block, 'VALUE',
|
||||
Blockly.Lua.ORDER_NONE) || '0';
|
||||
var varName = Blockly.Lua.variableDB_.getName(
|
||||
block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE);
|
||||
return varName + ' = ' + argument0 + '\n';
|
||||
};
|
74
lua_compressed.js
Normal file
74
lua_compressed.js
Normal file
File diff suppressed because one or more lines are too long
|
@ -62,7 +62,7 @@ Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_UNTIL = "kartok, kol pasieksi";
|
|||
Blockly.Msg.CONTROLS_WHILEUNTIL_OPERATOR_WHILE = "kartok kol";
|
||||
Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL = "Kartoja veiksmus, kol bus pasiekta nurodyta sąlyga.";
|
||||
Blockly.Msg.CONTROLS_WHILEUNTIL_TOOLTIP_WHILE = "Kartoja veiksmus, kol sąlyga tenkinama.";
|
||||
Blockly.Msg.DELETE_ALL_BLOCKS = "Delete all %1 blocks?"; // untranslated
|
||||
Blockly.Msg.DELETE_ALL_BLOCKS = "Ištrinti visus %1 blokus?";
|
||||
Blockly.Msg.DELETE_BLOCK = "Ištrinti bloką";
|
||||
Blockly.Msg.DELETE_X_BLOCKS = "Ištrinti %1 blokus";
|
||||
Blockly.Msg.DISABLE_BLOCK = "Išjungti bloką";
|
||||
|
|
|
@ -96,6 +96,7 @@
|
|||
"LOGIC_BOOLEAN_TOOLTIP": "Vrací pravda nebo nepravda.",
|
||||
"LOGIC_NULL": "prázdný",
|
||||
"LOGIC_NULL_TOOLTIP": "Vrátí prázdnou hodnotu",
|
||||
"LOGIC_TERNARY_HELPURL": "https://cs.wikipedia.org/wiki/Ternární operátor (programování)",
|
||||
"LOGIC_TERNARY_CONDITION": "test",
|
||||
"LOGIC_TERNARY_IF_TRUE": "pokud pravda",
|
||||
"LOGIC_TERNARY_IF_FALSE": "pokud nepravda",
|
||||
|
@ -297,12 +298,14 @@
|
|||
"VARIABLES_SET": "nastavit %1 na %2",
|
||||
"VARIABLES_SET_TOOLTIP": "Nastaví tuto proměnnou, aby se rovnala vstupu.",
|
||||
"VARIABLES_SET_CREATE_GET": "Vytvořit \"získat %1\"",
|
||||
"PROCEDURES_DEFNORETURN_HELPURL": "https://cs.wikipedia.org/w/index.php?title=Funkce_(programování)",
|
||||
"PROCEDURES_DEFNORETURN_TITLE": "k provedení",
|
||||
"PROCEDURES_DEFNORETURN_PROCEDURE": "proveď něco",
|
||||
"PROCEDURES_BEFORE_PARAMS": "s:",
|
||||
"PROCEDURES_CALL_BEFORE_PARAMS": "s:",
|
||||
"PROCEDURES_DEFNORETURN_TOOLTIP": "Vytvořit funkci bez výstupu.",
|
||||
"PROCEDURES_DEFNORETURN_COMMENT": "Popište tuto funkci...",
|
||||
"PROCEDURES_DEFRETURN_HELPURL": "https://cs.wikipedia.org/w/index.php?title=Funkce_(programování)",
|
||||
"PROCEDURES_DEFRETURN_RETURN": "navrátit",
|
||||
"PROCEDURES_DEFRETURN_TOOLTIP": "Vytvořit funkci s výstupem.",
|
||||
"PROCEDURES_ALLOW_STATEMENTS": "povolit příkazy",
|
||||
|
|
|
@ -9,14 +9,15 @@
|
|||
"Noamrotem",
|
||||
"Dvb",
|
||||
"LaG roiL",
|
||||
"아라"
|
||||
"아라",
|
||||
"Elyashiv"
|
||||
]
|
||||
},
|
||||
"VARIABLES_DEFAULT_NAME": "פריט",
|
||||
"TODAY": "היום",
|
||||
"DUPLICATE_BLOCK": "שכפל",
|
||||
"ADD_COMMENT": "הוסף תגובה",
|
||||
"REMOVE_COMMENT": "הסר הערה",
|
||||
"REMOVE_COMMENT": "הסר תגובה",
|
||||
"EXTERNAL_INPUTS": "קלטים חיצוניים",
|
||||
"INLINE_INPUTS": "קלטים פנימיים",
|
||||
"DELETE_BLOCK": "מחק קטע קוד",
|
||||
|
@ -87,6 +88,7 @@
|
|||
"LOGIC_OPERATION_TOOLTIP_OR": "תחזיר נכון אם מתקיים לפחות אחד מהקלטים נכונים.",
|
||||
"LOGIC_OPERATION_OR": "או",
|
||||
"LOGIC_NEGATE_TITLE": "לא %1",
|
||||
"LOGIC_NEGATE_TOOLTIP": "החזר אמת אם הקלט הוא שקר. החזר שקר אם הקלט אמת.",
|
||||
"LOGIC_BOOLEAN_TRUE": "נכון",
|
||||
"LOGIC_BOOLEAN_FALSE": "שגוי",
|
||||
"LOGIC_BOOLEAN_TOOLTIP": "תחזיר אם נכון או אם שגוי.",
|
||||
|
@ -96,6 +98,7 @@
|
|||
"LOGIC_TERNARY_IF_TRUE": "אם נכון",
|
||||
"LOGIC_TERNARY_IF_FALSE": "אם שגוי",
|
||||
"LOGIC_TERNARY_TOOLTIP": "בדוק את התנאי ב'מבחן'. אם התנאי נכון, תחזיר את הערך 'אם נכון'; אחרת תחזיר את הערך 'אם שגוי'.",
|
||||
"MATH_NUMBER_HELPURL": "https://he.wikipedia.org/wiki/מספר_ממשי",
|
||||
"MATH_NUMBER_TOOLTIP": "מספר.",
|
||||
"MATH_ADDITION_SYMBOL": "+",
|
||||
"MATH_SUBTRACTION_SYMBOL": "-",
|
||||
|
@ -112,11 +115,22 @@
|
|||
"MATH_ARITHMETIC_TOOLTIP_ADD": "תחזיר את סכום שני המספרים.",
|
||||
"MATH_ARITHMETIC_TOOLTIP_MINUS": "החזרת ההפרש בין שני מספרים.",
|
||||
"MATH_ARITHMETIC_TOOLTIP_MULTIPLY": "החזרת תוצאת הכפל בין שני מספרים.",
|
||||
"MATH_ARITHMETIC_TOOLTIP_DIVIDE": "החזרת המנה של שני המספרים.",
|
||||
"MATH_ARITHMETIC_TOOLTIP_POWER": "החזרת המספר הראשון בחזקת המספר השני.",
|
||||
"MATH_SINGLE_HELPURL": "https://he.wikipedia.org/wiki/שורש_ריבועי",
|
||||
"MATH_SINGLE_OP_ROOT": "שורש ריבועי",
|
||||
"MATH_SINGLE_TOOLTIP_ROOT": "החזרת השורש הריבועי של מספר.",
|
||||
"MATH_SINGLE_OP_ABSOLUTE": "ערך מוחלט",
|
||||
"MATH_SINGLE_TOOLTIP_ABS": "החזרת הערך המוחלט של מספר.",
|
||||
"MATH_SINGLE_TOOLTIP_NEG": "החזרת הערך הנגדי של מספר.",
|
||||
"MATH_SINGLE_TOOLTIP_LN": "החזרת הלוגריתם הטבעי של מספר.",
|
||||
"MATH_SINGLE_TOOLTIP_LOG10": "החזרת הלוגריתם לפי בסיס עשר של מספר.",
|
||||
"MATH_SINGLE_TOOLTIP_EXP": "החזרת e בחזקת מספר.",
|
||||
"MATH_SINGLE_TOOLTIP_POW10": "החזרת 10 בחזקת מספר.",
|
||||
"MATH_TRIG_HELPURL": "https://he.wikipedia.org/wiki/פונקציות_טריגונומטריות",
|
||||
"MATH_TRIG_TOOLTIP_SIN": "החזרת הסינוס של מעלה (לא רדיאן).",
|
||||
"MATH_TRIG_TOOLTIP_COS": "החזרת הקוסינוס של מעלה (לא רדיאן).",
|
||||
"MATH_TRIG_TOOLTIP_TAN": "החזרת הטנגס של מעלה (לא רדיאן).",
|
||||
"MATH_IS_EVEN": "זוגי",
|
||||
"MATH_IS_ODD": "אי-זוגי",
|
||||
"MATH_IS_PRIME": "ראשוני",
|
||||
|
@ -147,6 +161,8 @@
|
|||
"TEXT_CREATE_JOIN_TITLE_JOIN": "צירוף",
|
||||
"TEXT_APPEND_TO": "אל",
|
||||
"TEXT_APPEND_APPENDTEXT": "הוספת טקסט",
|
||||
"TEXT_GET_SUBSTRING_END_FROM_START": "לאות #",
|
||||
"TEXT_GET_SUBSTRING_END_FROM_END": "לאות # מהסוף",
|
||||
"TEXT_CHANGECASE_OPERATOR_UPPERCASE": "לאותיות גדולות (עבור טקסט באנגלית)",
|
||||
"TEXT_CHANGECASE_OPERATOR_LOWERCASE": "לאותיות קטנות (עבור טקסט באנגלית)",
|
||||
"TEXT_CHANGECASE_OPERATOR_TITLECASE": "לאותיות גדולות בתחילת כל מילה (עבור טקסט באנגלית)",
|
||||
|
@ -176,7 +192,7 @@
|
|||
"LISTS_INLIST": "ברשימה",
|
||||
"LISTS_INDEX_OF_FIRST": "מחזירה את המיקום הראשון של פריט ברשימה",
|
||||
"LISTS_INDEX_OF_LAST": "מחזירה את המיקום האחרון של פריט ברשימה",
|
||||
"LISTS_INDEX_OF_TOOLTIP": "מחזירה את האינדקס של המופע ראשון/אחרון של הפריט ברשימה. מחזירה 0 אם טקסט אינו נמצא.",
|
||||
"LISTS_INDEX_OF_TOOLTIP": "מחזירה את האינדקס של המופע ראשון/אחרון של הפריט ברשימה. מחזירה 0 אם הפריט אינו נמצא.",
|
||||
"LISTS_GET_INDEX_GET": "לקבל",
|
||||
"LISTS_GET_INDEX_GET_REMOVE": "קבל ומחק",
|
||||
"LISTS_GET_INDEX_REMOVE": "הסרה",
|
||||
|
|
226
msg/json/id.json
226
msg/json/id.json
|
@ -4,22 +4,25 @@
|
|||
"Kenrick95",
|
||||
"아라",
|
||||
"Mirws",
|
||||
"Marwan Mohamad"
|
||||
"Marwan Mohamad",
|
||||
"Kasimtan"
|
||||
]
|
||||
},
|
||||
"VARIABLES_DEFAULT_NAME": "item",
|
||||
"TODAY": "Hari ini",
|
||||
"DUPLICATE_BLOCK": "Duplikat",
|
||||
"ADD_COMMENT": "Tambahkan sebuah comment",
|
||||
"REMOVE_COMMENT": "Hapus komentar",
|
||||
"EXTERNAL_INPUTS": "Input-input eksternal",
|
||||
"INLINE_INPUTS": "Input inline",
|
||||
"ADD_COMMENT": "Tambahkan Komentar",
|
||||
"REMOVE_COMMENT": "Hapus Komentar",
|
||||
"EXTERNAL_INPUTS": "Input Eksternal",
|
||||
"INLINE_INPUTS": "Input Inline",
|
||||
"DELETE_BLOCK": "Hapus Blok",
|
||||
"DELETE_X_BLOCKS": "Hapus %1 Blok",
|
||||
"DELETE_ALL_BLOCKS": "Hapus semua %1 blok?",
|
||||
"CLEAN_UP": "Bersihkan Blok",
|
||||
"COLLAPSE_BLOCK": "Ciutkan Blok",
|
||||
"COLLAPSE_ALL": "Ciutkan Blok",
|
||||
"EXPAND_BLOCK": "Kembangkan Blok",
|
||||
"EXPAND_ALL": "Kembangkan blok-blok",
|
||||
"EXPAND_ALL": "Kembangkan Blok",
|
||||
"DISABLE_BLOCK": "Nonaktifkan Blok",
|
||||
"ENABLE_BLOCK": "Aktifkan Blok",
|
||||
"HELP": "Bantuan",
|
||||
|
@ -27,9 +30,9 @@
|
|||
"AUTH": "Silakan mengotorisasi aplikasi ini untuk memungkinkan pekerjaan Anda dapat disimpan dan digunakan bersama.",
|
||||
"ME": "Saya",
|
||||
"CHANGE_VALUE_TITLE": "Ubah nilai:",
|
||||
"NEW_VARIABLE": "Pembolehubah baru...",
|
||||
"NEW_VARIABLE_TITLE": "Nama pembolehubah baru:",
|
||||
"RENAME_VARIABLE": "namai ulang variabel...",
|
||||
"NEW_VARIABLE": "Variabel baru...",
|
||||
"NEW_VARIABLE_TITLE": "Nama variabel baru:",
|
||||
"RENAME_VARIABLE": "Ubah nama variabel...",
|
||||
"RENAME_VARIABLE_TITLE": "Ubah nama semua variabel '%1' menjadi:",
|
||||
"COLOUR_PICKER_HELPURL": "https://en.wikipedia.org/wiki/Color",
|
||||
"COLOUR_PICKER_TOOLTIP": "Pilih warna dari daftar warna.",
|
||||
|
@ -42,62 +45,62 @@
|
|||
"COLOUR_RGB_BLUE": "biru",
|
||||
"COLOUR_RGB_TOOLTIP": "Buatlah warna dengan jumlah yang ditentukan dari merah, hijau dan biru. Semua nilai harus antarai 0 sampai 100.",
|
||||
"COLOUR_BLEND_HELPURL": "http://meyerweb.com/eric/tools/color-blend/",
|
||||
"COLOUR_BLEND_TITLE": "Tertutup",
|
||||
"COLOUR_BLEND_COLOUR1": "Warna 1",
|
||||
"COLOUR_BLEND_COLOUR2": "Warna 2",
|
||||
"COLOUR_BLEND_TITLE": "campur",
|
||||
"COLOUR_BLEND_COLOUR1": "warna 1",
|
||||
"COLOUR_BLEND_COLOUR2": "warna 2",
|
||||
"COLOUR_BLEND_RATIO": "rasio",
|
||||
"COLOUR_BLEND_TOOLTIP": "mencampur dua warna secara bersamaan dengan perbandingan (0.0-1.0).",
|
||||
"COLOUR_BLEND_TOOLTIP": "Campur dua warna secara bersamaan dengan perbandingan (0.0 - 1.0).",
|
||||
"CONTROLS_REPEAT_HELPURL": "https://en.wikipedia.org/wiki/For_loop",
|
||||
"CONTROLS_REPEAT_TITLE": "ulangi %1 kali",
|
||||
"CONTROLS_REPEAT_INPUT_DO": "kerjakan",
|
||||
"CONTROLS_REPEAT_TOOLTIP": "Lakukan beberapa perintah beberapa kali.",
|
||||
"CONTROLS_WHILEUNTIL_OPERATOR_WHILE": "Ulangi jika",
|
||||
"CONTROLS_WHILEUNTIL_OPERATOR_UNTIL": "Ulangi sampai",
|
||||
"CONTROLS_WHILEUNTIL_TOOLTIP_WHILE": "Jika sementara nilai benar (true), maka lakukan beberapa perintah.",
|
||||
"CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL": "Jika sementara nilai tidak benar (false), maka lakukan beberapa perintah.",
|
||||
"CONTROLS_WHILEUNTIL_OPERATOR_WHILE": "ulangi jika",
|
||||
"CONTROLS_WHILEUNTIL_OPERATOR_UNTIL": "ulangi sampai",
|
||||
"CONTROLS_WHILEUNTIL_TOOLTIP_WHILE": "Selagi nilainya benar, maka lakukan beberapa perintah.",
|
||||
"CONTROLS_WHILEUNTIL_TOOLTIP_UNTIL": "Selagi nilainya salah, maka lakukan beberapa perintah.",
|
||||
"CONTROLS_FOR_TOOLTIP": "Menggunakan variabel \"%1\" dengan mengambil nilai dari batas awal hingga ke batas akhir, dengan interval tertentu, dan mengerjakan block tertentu.",
|
||||
"CONTROLS_FOR_TITLE": "Cacah dengan %1 dari %2 ke %3 dengan step / penambahan %4",
|
||||
"CONTROLS_FOREACH_TITLE": "untuk setiap item %1 di dalam list %2",
|
||||
"CONTROLS_FOREACH_TOOLTIP": "Untuk tiap-tiap item di dalam list, tetapkan variabel '%1' ke dalam item, selanjutnya kerjakan beberapa statement.",
|
||||
"CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK": "Keluar dari perulangan",
|
||||
"CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE": "Lanjutkan dengan langkah penggulangan berikutnya",
|
||||
"CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK": "Keluar sementara dari perulanggan.",
|
||||
"CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE": "Abaikan sisa dari loop ini, dan lanjutkan dengan iterasi berikutnya.",
|
||||
"CONTROLS_FLOW_STATEMENTS_WARNING": "Peringatan: Blok ini hanya dapat digunakan dalam loop.",
|
||||
"CONTROLS_IF_TOOLTIP_1": "jika nilainya benar maka kerjakan perintah berikutnya.",
|
||||
"CONTROLS_IF_TOOLTIP_2": "jika nilainya benar, maka kerjakan blok perintah yang pertama. Jika tidak, kerjakan blok perintah yang kedua.",
|
||||
"CONTROLS_IF_TOOLTIP_3": "Jika nilai pertama adalah benar (true), maka lakukan perintah-perintah yang berada didalam blok pertama. Jika nilai kedua adalah benar (true), maka lakukan perintah-perintah yang berada didalam blok kedua.",
|
||||
"CONTROLS_IF_TOOLTIP_4": "Jika blok pertama adalah benar (true), maka lakukan perintah-perintah yang berada didalam blok pertama. Atau jika blok kedua adalah benar (true), maka lakukan perintah-perintah yang berada didalam blok kedua.",
|
||||
"CONTROLS_IF_MSG_IF": "Jika",
|
||||
"CONTROLS_IF_MSG_ELSEIF": "else if",
|
||||
"CONTROLS_IF_MSG_ELSE": "else",
|
||||
"CONTROLS_IF_IF_TOOLTIP": "Menambahkan, menghapus, atau menyusun kembali bagian untuk mengkonfigurasi blok IF ini.",
|
||||
"CONTROLS_IF_ELSEIF_TOOLTIP": "tambahkan prasyarat ke dalam blok IF.",
|
||||
"CONTROLS_IF_ELSE_TOOLTIP": "Terakhir, tambahkan tangkap-semua kondisi kedalam blok jika (if).",
|
||||
"CONTROLS_FLOW_STATEMENTS_OPERATOR_BREAK": "keluar dari perulangan",
|
||||
"CONTROLS_FLOW_STATEMENTS_OPERATOR_CONTINUE": "lanjutkan dengan langkah perulangan berikutnya",
|
||||
"CONTROLS_FLOW_STATEMENTS_TOOLTIP_BREAK": "Keluar dari perulangan.",
|
||||
"CONTROLS_FLOW_STATEMENTS_TOOLTIP_CONTINUE": "Abaikan sisa dari perulangan ini, dan lanjutkan dengan langkah berikutnya.",
|
||||
"CONTROLS_FLOW_STATEMENTS_WARNING": "Peringatan: Blok ini hanya dapat digunakan dalam perulangan.",
|
||||
"CONTROLS_IF_TOOLTIP_1": "Jika nilainya benar, maka lakukan beberapa perintah.",
|
||||
"CONTROLS_IF_TOOLTIP_2": "Jika nilainya benar, maka kerjakan perintah blok pertama. Jika tidak, kerjakan perintah blok kedua.",
|
||||
"CONTROLS_IF_TOOLTIP_3": "Jika nilai pertama benar, maka kerjakan perintah blok pertama. Sebaliknya, jika nilai kedua benar, kerjakan perintah blok kedua.",
|
||||
"CONTROLS_IF_TOOLTIP_4": "Jika nilai pertama benar, maka kerjakan perintah blok pertama. Sebaliknya, jika nilai kedua benar, kerjakan perintah blok kedua. Jika dua-duanya tidak benar, kerjakan perintah blok terakhir.",
|
||||
"CONTROLS_IF_MSG_IF": "jika",
|
||||
"CONTROLS_IF_MSG_ELSEIF": "atau jika",
|
||||
"CONTROLS_IF_MSG_ELSE": "lainnya",
|
||||
"CONTROLS_IF_IF_TOOLTIP": "Tambahkan, hapus, atau susun kembali bagian untuk mengkonfigurasi blok IF ini.",
|
||||
"CONTROLS_IF_ELSEIF_TOOLTIP": "Tambahkan prasyarat ke dalam blok IF.",
|
||||
"CONTROLS_IF_ELSE_TOOLTIP": "Terakhir, tambahkan kondisi tangkap-semua kedalam blok IF.",
|
||||
"LOGIC_COMPARE_HELPURL": "https://en.wikipedia.org/wiki/Inequality_(mathematics)",
|
||||
"LOGIC_COMPARE_TOOLTIP_EQ": "Mengembalikan betul jika input kedua-duanya sama dengan satu sama lain.",
|
||||
"LOGIC_COMPARE_TOOLTIP_NEQ": "Mengembalikan nilai benar (true) jika kedua input tidak sama satu dengan yang lain.",
|
||||
"LOGIC_COMPARE_TOOLTIP_LT": "Mengembalikan nilai benar (true) jika input yang pertama lebih kecil dari input yang kedua.",
|
||||
"LOGIC_COMPARE_TOOLTIP_LTE": "Mengembalikan nilai benar (true) jika input yang pertama lebih kecil atau sama dengan input yang kedua .",
|
||||
"LOGIC_COMPARE_TOOLTIP_GT": "Mengembalikan nilai benar (true) jika input yang pertama lebih besar dari input yang kedua.",
|
||||
"LOGIC_COMPARE_TOOLTIP_GTE": "Mengembalikan nilai benar (true) jika input yang pertama lebih besar dari atau sama dengan input yang kedua.",
|
||||
"LOGIC_OPERATION_TOOLTIP_AND": "Kembalikan betul jika kedua-dua input adalah betul.",
|
||||
"LOGIC_COMPARE_TOOLTIP_EQ": "Kembalikan benar jika kedua input sama satu dengan lainnya.",
|
||||
"LOGIC_COMPARE_TOOLTIP_NEQ": "Kembalikan benar jika kedua input tidak sama satu dengan lainnya.",
|
||||
"LOGIC_COMPARE_TOOLTIP_LT": "Kembalikan benar jika input pertama lebih kecil dari input kedua.",
|
||||
"LOGIC_COMPARE_TOOLTIP_LTE": "Kembalikan benar jika input pertama lebih kecil atau sama dengan input kedua .",
|
||||
"LOGIC_COMPARE_TOOLTIP_GT": "Kembalikan benar jika input pertama lebih besar dari input kedua.",
|
||||
"LOGIC_COMPARE_TOOLTIP_GTE": "Kembalikan benar jika input pertama lebih besar dari atau sama dengan input kedua.",
|
||||
"LOGIC_OPERATION_TOOLTIP_AND": "Kembalikan benar jika kedua input adalah benar.",
|
||||
"LOGIC_OPERATION_AND": "dan",
|
||||
"LOGIC_OPERATION_TOOLTIP_OR": "Mengembalikan nilai benar (true) jika setidaknya salah satu masukan nilainya benar (true).",
|
||||
"LOGIC_OPERATION_TOOLTIP_OR": "Kembalikan benar jika minimal satu input nilainya benar.",
|
||||
"LOGIC_OPERATION_OR": "atau",
|
||||
"LOGIC_NEGATE_TITLE": "bukan (not) %1",
|
||||
"LOGIC_NEGATE_TOOLTIP": "Mengembalikan nilai benar (true) jika input false. Mengembalikan nilai salah (false) jika input true.",
|
||||
"LOGIC_BOOLEAN_TRUE": "Benar",
|
||||
"LOGIC_BOOLEAN_FALSE": "Salah",
|
||||
"LOGIC_BOOLEAN_TOOLTIP": "Mengembalikan betul (true) atau salah (false).",
|
||||
"LOGIC_NEGATE_TOOLTIP": "Kembalikan benar jika input salah. Kembalikan salah jika input benar.",
|
||||
"LOGIC_BOOLEAN_TRUE": "benar",
|
||||
"LOGIC_BOOLEAN_FALSE": "salah",
|
||||
"LOGIC_BOOLEAN_TOOLTIP": "Kembalikan benar atau salah.",
|
||||
"LOGIC_NULL_HELPURL": "https://en.wikipedia.org/wiki/Nullable_type",
|
||||
"LOGIC_NULL": "null",
|
||||
"LOGIC_NULL_TOOLTIP": "mengembalikan kosong.",
|
||||
"LOGIC_NULL_TOOLTIP": "Kembalikan null.",
|
||||
"LOGIC_TERNARY_HELPURL": "https://en.wikipedia.org/wiki/%3F:",
|
||||
"LOGIC_TERNARY_CONDITION": "test",
|
||||
"LOGIC_TERNARY_IF_TRUE": "jika benar (true)",
|
||||
"LOGIC_TERNARY_IF_FALSE": "jika tidak benar (false)",
|
||||
"LOGIC_TERNARY_TOOLTIP": "Periksa kondisi di \"test\". Jika kondisi benar (true), mengembalikan nilai \"jika benar\" ; Jik sebaliknya akan mengembalikan nilai \"jika salah\".",
|
||||
"LOGIC_TERNARY_IF_TRUE": "jika benar",
|
||||
"LOGIC_TERNARY_IF_FALSE": "jika salah",
|
||||
"LOGIC_TERNARY_TOOLTIP": "Periksa kondisi di 'test'. Jika kondisi benar, kembalikan nilai 'if true'; jika sebaliknya kembalikan nilai 'if false'.",
|
||||
"MATH_NUMBER_HELPURL": "https://en.wikipedia.org/wiki/Number",
|
||||
"MATH_NUMBER_TOOLTIP": "Suatu angka.",
|
||||
"MATH_ADDITION_SYMBOL": "+",
|
||||
|
@ -129,8 +132,8 @@
|
|||
"MATH_SINGLE_TOOLTIP_POW10": "Kembalikan 10 pangkat angka.",
|
||||
"MATH_TRIG_HELPURL": "https://en.wikipedia.org/wiki/Trigonometric_functions",
|
||||
"MATH_TRIG_TOOLTIP_SIN": "Kembalikan sinus dari derajat (bukan radian).",
|
||||
"MATH_TRIG_TOOLTIP_COS": "Kembalikan cos dari derajat (bukan radian).",
|
||||
"MATH_TRIG_TOOLTIP_TAN": "Kembalikan tangen dari derajat (tidak radian).",
|
||||
"MATH_TRIG_TOOLTIP_COS": "Kembalikan cosinus dari derajat (bukan radian).",
|
||||
"MATH_TRIG_TOOLTIP_TAN": "Kembalikan tangen dari derajat (bukan radian).",
|
||||
"MATH_TRIG_TOOLTIP_ASIN": "Kembalikan asin dari angka.",
|
||||
"MATH_TRIG_TOOLTIP_ACOS": "Kembalikan acosine dari angka.",
|
||||
"MATH_TRIG_TOOLTIP_ATAN": "Kembalikan atan dari angka.",
|
||||
|
@ -140,48 +143,48 @@
|
|||
"MATH_IS_ODD": "adalah bilangan ganjil",
|
||||
"MATH_IS_PRIME": "adalah bilangan pokok",
|
||||
"MATH_IS_WHOLE": "adalah bilangan bulat",
|
||||
"MATH_IS_POSITIVE": "adalah bilangan positif",
|
||||
"MATH_IS_NEGATIVE": "adalah bilangan negatif",
|
||||
"MATH_IS_DIVISIBLE_BY": "dibagi oleh",
|
||||
"MATH_IS_TOOLTIP": "Periksa apakah angka adalah bilangan genap, bilangan pokok, bilangan bulat, bilangan positif, bilangan negatif, atau apakan bisa dibagi oleh angka tertentu. Mengembalikan benar (true) atau salah (false).",
|
||||
"MATH_IS_POSITIVE": "adalah bilangan positif",
|
||||
"MATH_IS_NEGATIVE": "adalah bilangan negatif",
|
||||
"MATH_IS_DIVISIBLE_BY": "dapat dibagi oleh",
|
||||
"MATH_IS_TOOLTIP": "Periksa apakah angka adalah bilangan genap, bilangan ganjil, bilangan pokok, bilangan bulat, bilangan positif, bilangan negatif, atau apakan bisa dibagi oleh angka tertentu. Kembalikan benar atau salah.",
|
||||
"MATH_CHANGE_HELPURL": "https://en.wikipedia.org/wiki/Programming_idiom#Incrementing_a_counter",
|
||||
"MATH_CHANGE_TITLE": "ubah %1 oleh %2",
|
||||
"MATH_CHANGE_TOOLTIP": "Tambahkan angka kedalam variabel '%1'.",
|
||||
"MATH_ROUND_HELPURL": "https://en.wikipedia.org/wiki/Rounding",
|
||||
"MATH_ROUND_TOOLTIP": "Bulatkan suatu bilangan naik atau turun.",
|
||||
"MATH_ROUND_OPERATOR_ROUND": "membulatkan",
|
||||
"MATH_ROUND_OPERATOR_ROUNDUP": "mengumpulkan",
|
||||
"MATH_ROUND_OPERATOR_ROUNDUP": "membulatkan keatas",
|
||||
"MATH_ROUND_OPERATOR_ROUNDDOWN": "membulatkan kebawah",
|
||||
"MATH_ONLIST_OPERATOR_SUM": "jumlah dari list (daftar)",
|
||||
"MATH_ONLIST_OPERATOR_SUM": "jumlah dari list",
|
||||
"MATH_ONLIST_TOOLTIP_SUM": "Kembalikan jumlah dari seluruh bilangan dari list.",
|
||||
"MATH_ONLIST_OPERATOR_MIN": "minimum dari list (daftar)",
|
||||
"MATH_ONLIST_OPERATOR_MIN": "minimum dari list",
|
||||
"MATH_ONLIST_TOOLTIP_MIN": "Kembalikan angka terkecil dari list.",
|
||||
"MATH_ONLIST_OPERATOR_MAX": "maximum dari list (daftar)",
|
||||
"MATH_ONLIST_OPERATOR_MAX": "maksimum dari list",
|
||||
"MATH_ONLIST_TOOLTIP_MAX": "Kembalikan angka terbesar dari list.",
|
||||
"MATH_ONLIST_OPERATOR_AVERAGE": "rata-rata dari list (daftar)",
|
||||
"MATH_ONLIST_TOOLTIP_AVERAGE": "Kembalikan rata-rata (mean aritmetik) dari nilai numerik dari list (daftar).",
|
||||
"MATH_ONLIST_OPERATOR_MEDIAN": "median dari list (daftar)",
|
||||
"MATH_ONLIST_OPERATOR_AVERAGE": "rata-rata dari list",
|
||||
"MATH_ONLIST_TOOLTIP_AVERAGE": "Kembalikan rata-rata (mean aritmetik) dari nilai numerik dari list.",
|
||||
"MATH_ONLIST_OPERATOR_MEDIAN": "median dari list",
|
||||
"MATH_ONLIST_TOOLTIP_MEDIAN": "Kembalikan median dari list.",
|
||||
"MATH_ONLIST_OPERATOR_MODE": "mode-mode dari list (daftar)",
|
||||
"MATH_ONLIST_TOOLTIP_MODE": "Kembalikan list berisi item-item yang paling umum dari dalam list.",
|
||||
"MATH_ONLIST_OPERATOR_STD_DEV": "deviasi standar dari list (daftar)",
|
||||
"MATH_ONLIST_OPERATOR_MODE": "mode-mode dari list",
|
||||
"MATH_ONLIST_TOOLTIP_MODE": "Kembalikan list berisi item yang paling umum dari dalam list.",
|
||||
"MATH_ONLIST_OPERATOR_STD_DEV": "deviasi standar dari list",
|
||||
"MATH_ONLIST_TOOLTIP_STD_DEV": "Kembalikan standard deviasi dari list.",
|
||||
"MATH_ONLIST_OPERATOR_RANDOM": "item acak dari list (daftar)",
|
||||
"MATH_ONLIST_TOOLTIP_RANDOM": "Kembalikan element acak dari list.",
|
||||
"MATH_ONLIST_OPERATOR_RANDOM": "item acak dari list",
|
||||
"MATH_ONLIST_TOOLTIP_RANDOM": "Kembalikan elemen acak dari list.",
|
||||
"MATH_MODULO_HELPURL": "https://en.wikipedia.org/wiki/Modulo_operation",
|
||||
"MATH_MODULO_TITLE": "sisa %1 ÷ %2",
|
||||
"MATH_MODULO_TITLE": "sisa dari %1 ÷ %2",
|
||||
"MATH_MODULO_TOOLTIP": "Kembalikan sisa dari pembagian ke dua angka.",
|
||||
"MATH_CONSTRAIN_TITLE": "Batasi %1 rendah %2 tinggi %3",
|
||||
"MATH_CONSTRAIN_TOOLTIP": "Batasi angka antara batas yang ditentukan (inklusif).",
|
||||
"MATH_RANDOM_INT_HELPURL": "https://en.wikipedia.org/wiki/Random_number_generation",
|
||||
"MATH_RANDOM_INT_TITLE": "acak bulat dari %1 sampai %2",
|
||||
"MATH_RANDOM_INT_TOOLTIP": "Mengembalikan bilangan acak antara dua batas yang ditentukan, inklusif.",
|
||||
"MATH_RANDOM_INT_TOOLTIP": "Kembalikan bilangan acak antara dua batas yang ditentukan, inklusif.",
|
||||
"MATH_RANDOM_FLOAT_HELPURL": "https://en.wikipedia.org/wiki/Random_number_generation",
|
||||
"MATH_RANDOM_FLOAT_TITLE_RANDOM": "Nilai pecahan acak",
|
||||
"MATH_RANDOM_FLOAT_TOOLTIP": "Mengembalikan nilai acak pecahan antara 0.0 (inklusif) dan 1.0 (ekslusif).",
|
||||
"MATH_RANDOM_FLOAT_TITLE_RANDOM": "nilai pecahan acak",
|
||||
"MATH_RANDOM_FLOAT_TOOLTIP": "Kembalikan nilai pecahan acak antara 0.0 (inklusif) dan 1.0 (eksklusif).",
|
||||
"TEXT_TEXT_HELPURL": "https://en.wikipedia.org/wiki/String_(computer_science)",
|
||||
"TEXT_TEXT_TOOLTIP": "Huruf, kata atau baris teks.",
|
||||
"TEXT_JOIN_TITLE_CREATEWITH": "Buat teks dengan",
|
||||
"TEXT_JOIN_TITLE_CREATEWITH": "buat teks dengan",
|
||||
"TEXT_JOIN_TOOLTIP": "Buat teks dengan cara gabungkan sejumlah item.",
|
||||
"TEXT_CREATE_JOIN_TITLE_JOIN": "join",
|
||||
"TEXT_CREATE_JOIN_TOOLTIP": "Tambah, ambil, atau susun ulang teks blok.",
|
||||
|
@ -192,7 +195,7 @@
|
|||
"TEXT_LENGTH_TITLE": "panjang dari %1",
|
||||
"TEXT_LENGTH_TOOLTIP": "Kembalikan sejumlah huruf (termasuk spasi) dari teks yang disediakan.",
|
||||
"TEXT_ISEMPTY_TITLE": "%1 kosong",
|
||||
"TEXT_ISEMPTY_TOOLTIP": "Kembalikan benar (true) jika teks yang disediakan kosong.",
|
||||
"TEXT_ISEMPTY_TOOLTIP": "Kembalikan benar jika teks yang disediakan kosong.",
|
||||
"TEXT_INDEXOF_TOOLTIP": "Kembalikan indeks pertama dan terakhir dari kejadian pertama/terakhir dari teks pertama dalam teks kedua. Kembalikan 0 jika teks tidak ditemukan.",
|
||||
"TEXT_INDEXOF_INPUT_INTEXT": "dalam teks",
|
||||
"TEXT_INDEXOF_OPERATOR_FIRST": "temukan kejadian pertama dalam teks",
|
||||
|
@ -204,7 +207,7 @@
|
|||
"TEXT_CHARAT_LAST": "ambil huruf terakhir",
|
||||
"TEXT_CHARAT_RANDOM": "ambil huruf secara acak",
|
||||
"TEXT_CHARAT_TOOLTIP": "Kembalikan karakter dari posisi tertentu.",
|
||||
"TEXT_GET_SUBSTRING_TOOLTIP": "Mengembalikan spesifik bagian dari teks.",
|
||||
"TEXT_GET_SUBSTRING_TOOLTIP": "Kembalikan spesifik bagian dari teks.",
|
||||
"TEXT_GET_SUBSTRING_INPUT_IN_TEXT": "in teks",
|
||||
"TEXT_GET_SUBSTRING_START_FROM_START": "ambil bagian teks (substring) dari huruf no #",
|
||||
"TEXT_GET_SUBSTRING_START_FROM_END": "ambil bagian teks (substring) dari huruf ke # dari terakhir",
|
||||
|
@ -228,22 +231,22 @@
|
|||
"TEXT_PROMPT_TOOLTIP_TEXT": "Meminta pengguna untuk memberi beberapa teks.",
|
||||
"LISTS_CREATE_EMPTY_HELPURL": "https://github.com/google/blockly/wiki/Lists#create-empty-list",
|
||||
"LISTS_CREATE_EMPTY_TITLE": "buat list kosong",
|
||||
"LISTS_CREATE_EMPTY_TOOLTIP": "Mengembalikan daftar, dengan panjang 0, tidak berisi data",
|
||||
"LISTS_CREATE_WITH_TOOLTIP": "Buat sebuah daftar (list) dengan sejumlah item.",
|
||||
"LISTS_CREATE_WITH_INPUT_WITH": "buat daftar (list) dengan",
|
||||
"LISTS_CREATE_EMPTY_TOOLTIP": "Kembalikan list, dengan panjang 0, tidak berisi data",
|
||||
"LISTS_CREATE_WITH_TOOLTIP": "Buat sebuah list dengan sejumlah item.",
|
||||
"LISTS_CREATE_WITH_INPUT_WITH": "buat list dengan",
|
||||
"LISTS_CREATE_WITH_CONTAINER_TITLE_ADD": "list",
|
||||
"LISTS_CREATE_WITH_CONTAINER_TOOLTIP": "Tambahkan, hapus, atau susun ulang bagian untuk mengkonfigurasi blok LIST (daftar) ini.",
|
||||
"LISTS_CREATE_WITH_ITEM_TOOLTIP": "Tambahkan sebuah item ke daftar (list).",
|
||||
"LISTS_REPEAT_TOOLTIP": "Ciptakan daftar yang terdiri dari nilai yang diberikan diulang jumlah waktu yang ditentukan.",
|
||||
"LISTS_REPEAT_TITLE": "membuat daftar dengan item %1 diulang %2 kali",
|
||||
"LISTS_CREATE_WITH_CONTAINER_TOOLTIP": "Tambahkan, hapus, atau susun ulang bagian untuk mengkonfigurasi blok list ini.",
|
||||
"LISTS_CREATE_WITH_ITEM_TOOLTIP": "Tambahkan sebuah item ke list.",
|
||||
"LISTS_REPEAT_TOOLTIP": "Buat sebuah list yang terdiri dari nilai yang diberikan diulang sebanyak jumlah yang ditentukan.",
|
||||
"LISTS_REPEAT_TITLE": "buat list dengan item %1 diulang %2 kali",
|
||||
"LISTS_LENGTH_TITLE": "panjang dari %1",
|
||||
"LISTS_LENGTH_TOOLTIP": "Mengembalikan panjang daftar.",
|
||||
"LISTS_LENGTH_TOOLTIP": "Kembalikan panjang list.",
|
||||
"LISTS_ISEMPTY_TITLE": "%1 kosong",
|
||||
"LISTS_ISEMPTY_TOOLTIP": "Mengembalikan nilai benar (true) jika list kosong.",
|
||||
"LISTS_INLIST": "dalam daftar",
|
||||
"LISTS_ISEMPTY_TOOLTIP": "Kembalikan benar jika list kosong.",
|
||||
"LISTS_INLIST": "dalam list",
|
||||
"LISTS_INDEX_OF_FIRST": "cari kejadian pertama item",
|
||||
"LISTS_INDEX_OF_LAST": "Cari kejadian terakhir item",
|
||||
"LISTS_INDEX_OF_TOOLTIP": "Mengembalikan indeks dari kejadian pertama/terakhir item dalam daftar. Menghasilkan 0 jika teks tidak ditemukan.",
|
||||
"LISTS_INDEX_OF_LAST": "cari kejadian terakhir item",
|
||||
"LISTS_INDEX_OF_TOOLTIP": "Kembalikan indeks dari item pertama/terakhir kali muncul dalam list. Kembalikan 0 jika item tidak ditemukan.",
|
||||
"LISTS_GET_INDEX_GET": "dapatkan",
|
||||
"LISTS_GET_INDEX_GET_REMOVE": "dapatkan dan hapus",
|
||||
"LISTS_GET_INDEX_REMOVE": "Hapus",
|
||||
|
@ -252,21 +255,21 @@
|
|||
"LISTS_GET_INDEX_FIRST": "pertama",
|
||||
"LISTS_GET_INDEX_LAST": "terakhir",
|
||||
"LISTS_GET_INDEX_RANDOM": "acak",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_FROM_START": "Sisipkan item ke dalam posisi yang telah ditentukan didalam list (daftar). Item pertama adalah item terakhir (yg paling akhir).",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_FROM_END": "Sisipkan item ke dalam posisi yang telah ditentukan didalam list (daftar). Item pertama adalah item yang terakhir.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_FIRST": "Kembalikan item pertama dalam daftar (list).",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_LAST": "Mengembalikan item pertama dalam list (daftar).",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_RANDOM": "Mengembalikan item acak dalam list (daftar).",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM_START": "Menghilangkan dan mengembalikan barang di posisi tertentu dalam list (daftar). #1 adalah item pertama.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM_END": "Menghilangkan dan mengembalikan barang di posisi tertentu dalam list (daftar). #1 adalah item terakhir.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST": "Menghilangkan dan mengembalikan item pertama dalam list (daftar).",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST": "Menghilangkan dan mengembalikan item terakhir dalam list (daftar).",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM": "Menghilangkan dan mengembalikan barang dengan acak dalam list (daftar).",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM_START": "Menghapus item dengan posisi tertentu dalam daftar. Item pertama adalah item yang terakhir.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM_END": "Menghapus item dengan posisi tertentu dalam daftar. Item pertama adalah item yang terakhir.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST": "Menghapus item pertama dalam daftar.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST": "Menghapus item terakhir dalam daftar.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM": "Menghapus sebuah item secara acak dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_FROM_START": "Kembalikan item di posisi tertentu dalam list. #1 adalah item pertama.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_FROM_END": "Kembalikan item di posisi tertentu dalam list. #1 adalah item terakhir.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_FIRST": "Kembalikan item pertama dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_LAST": "Kembalikan item terakhir dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_RANDOM": "Kembalikan item acak dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM_START": "Hapus dan kembalikan item di posisi tertentu dalam list. #1 adalah item pertama.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FROM_END": "Hapus dan kembalikan item di posisi tertentu dalam list. #1 adalah item terakhir.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_FIRST": "Hapus dan kembalikan item pertama dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_LAST": "Hapus dan kembalikan item terakhir dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_GET_REMOVE_RANDOM": "Hapus dan kembalikan item acak dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM_START": "Hapus item di posisi tertentu dalam list. #1 adalah item pertama.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_FROM_END": "Hapus item di posisi tertentu dalam list. #1 adalah item terakhir.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_FIRST": "Hapus item pertama dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_LAST": "Hapus item terakhir dalam list.",
|
||||
"LISTS_GET_INDEX_TOOLTIP_REMOVE_RANDOM": "Hapus sebuah item acak dalam list.",
|
||||
"LISTS_SET_INDEX_SET": "tetapkan",
|
||||
"LISTS_SET_INDEX_INSERT": "sisipkan di",
|
||||
"LISTS_SET_INDEX_INPUT_TO": "sebagai",
|
||||
|
@ -280,32 +283,33 @@
|
|||
"LISTS_SET_INDEX_TOOLTIP_INSERT_FIRST": "Sisipkan item di bagian awal dari list.",
|
||||
"LISTS_SET_INDEX_TOOLTIP_INSERT_LAST": "Tambahkan item ke bagian akhir list.",
|
||||
"LISTS_SET_INDEX_TOOLTIP_INSERT_RANDOM": "Sisipkan item secara acak ke dalam list.",
|
||||
"LISTS_GET_SUBLIST_START_FROM_START": "Dapatkan bagian daftar dari #",
|
||||
"LISTS_GET_SUBLIST_START_FROM_END": "Dapatkan bagian list nomor # dari akhir",
|
||||
"LISTS_GET_SUBLIST_START_FIRST": "Dapatkan bagian pertama dari list",
|
||||
"LISTS_GET_SUBLIST_START_FROM_START": "dapatkan sub-list dari #",
|
||||
"LISTS_GET_SUBLIST_START_FROM_END": "dapatkan sub-list dari nomor # dari akhir",
|
||||
"LISTS_GET_SUBLIST_START_FIRST": "dapatkan sub-list dari pertama",
|
||||
"LISTS_GET_SUBLIST_END_FROM_START": "ke #",
|
||||
"LISTS_GET_SUBLIST_END_FROM_END": "ke # dari akhir",
|
||||
"LISTS_GET_SUBLIST_END_LAST": "ke yang paling akhir",
|
||||
"LISTS_GET_SUBLIST_TOOLTIP": "Membuat salinan dari bagian tertentu dari list.",
|
||||
"LISTS_SPLIT_LIST_FROM_TEXT": "membuat daftar dari teks",
|
||||
"LISTS_SPLIT_TEXT_FROM_LIST": "buat teks dari daftar",
|
||||
"LISTS_GET_SUBLIST_TOOLTIP": "Buat salinan bagian tertentu dari list.",
|
||||
"LISTS_SPLIT_LIST_FROM_TEXT": "buat list dari teks",
|
||||
"LISTS_SPLIT_TEXT_FROM_LIST": "buat teks dari list",
|
||||
"LISTS_SPLIT_WITH_DELIMITER": "dengan pembatas",
|
||||
"LISTS_SPLIT_TOOLTIP_SPLIT": "Membagi teks ke dalam daftar teks, pisahkan pada setiap pembatas.",
|
||||
"LISTS_SPLIT_TOOLTIP_JOIN": "Gabung daftar teks menjadi satu teks, yang dipisahkan oleh pembatas.",
|
||||
"VARIABLES_GET_TOOLTIP": "Mengembalikan nilai variabel ini.",
|
||||
"VARIABLES_GET_CREATE_SET": "Membuat 'tetapkan %1'",
|
||||
"VARIABLES_GET_TOOLTIP": "Kembalikan nilai variabel ini.",
|
||||
"VARIABLES_GET_CREATE_SET": "Buat 'set %1'",
|
||||
"VARIABLES_SET": "tetapkan %1 untuk %2",
|
||||
"VARIABLES_SET_TOOLTIP": "tetapkan variabel ini dengan input yang sama.",
|
||||
"VARIABLES_SET_CREATE_GET": "Membuat 'dapatkan %1'",
|
||||
"VARIABLES_SET_CREATE_GET": "Buat 'get %1'",
|
||||
"PROCEDURES_DEFNORETURN_HELPURL": "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29",
|
||||
"PROCEDURES_DEFNORETURN_TITLE": "untuk",
|
||||
"PROCEDURES_DEFNORETURN_PROCEDURE": "buat sesuatu",
|
||||
"PROCEDURES_BEFORE_PARAMS": "dengan:",
|
||||
"PROCEDURES_CALL_BEFORE_PARAMS": "dengan:",
|
||||
"PROCEDURES_DEFNORETURN_TOOLTIP": "Menciptakan sebuah fungsi dengan tiada output.",
|
||||
"PROCEDURES_DEFNORETURN_TOOLTIP": "Buat sebuah fungsi tanpa output.",
|
||||
"PROCEDURES_DEFNORETURN_COMMENT": "Jelaskan fungsi ini...",
|
||||
"PROCEDURES_DEFRETURN_HELPURL": "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29",
|
||||
"PROCEDURES_DEFRETURN_RETURN": "kembali",
|
||||
"PROCEDURES_DEFRETURN_TOOLTIP": "Menciptakan sebuah fungsi dengan satu output.",
|
||||
"PROCEDURES_DEFRETURN_TOOLTIP": "Buat sebuah fungsi dengan satu output.",
|
||||
"PROCEDURES_ALLOW_STATEMENTS": "memungkinkan pernyataan",
|
||||
"PROCEDURES_DEF_DUPLICATE_WARNING": "Peringatan: Fungsi ini memiliki parameter duplikat.",
|
||||
"PROCEDURES_CALLNORETURN_HELPURL": "https://en.wikipedia.org/wiki/Procedure_%28computer_science%29",
|
||||
|
|
|
@ -15,6 +15,7 @@
|
|||
"INLINE_INPUTS": "Išdėstyti vienoje eilutėje",
|
||||
"DELETE_BLOCK": "Ištrinti bloką",
|
||||
"DELETE_X_BLOCKS": "Ištrinti %1 blokus",
|
||||
"DELETE_ALL_BLOCKS": "Ištrinti visus %1 blokus?",
|
||||
"CLEAN_UP": "Išvalyti blokus",
|
||||
"COLLAPSE_BLOCK": "Suskleisti bloką",
|
||||
"COLLAPSE_ALL": "Suskleisti blokus",
|
||||
|
|
|
@ -49,6 +49,18 @@
|
|||
<script src="../../generators/dart/variables.js"></script>
|
||||
<script src="../../generators/dart/procedures.js"></script>
|
||||
|
||||
<script src="../../generators/lua.js"></script>
|
||||
<script src="unittest_lua.js"></script>
|
||||
<script src="../../generators/lua/logic.js"></script>
|
||||
<script src="../../generators/lua/loops.js"></script>
|
||||
<script src="../../generators/lua/math.js"></script>
|
||||
<script src="../../generators/lua/text.js"></script>
|
||||
<script src="../../generators/lua/lists.js"></script>
|
||||
<script src="../../generators/lua/colour.js"></script>
|
||||
<script src="../../generators/lua/variables.js"></script>
|
||||
<script src="../../generators/lua/procedures.js"></script>
|
||||
|
||||
|
||||
<script src="unittest.js"></script>
|
||||
<script src="../../msg/messages.js"></script>
|
||||
<script src="../../blocks/logic.js"></script>
|
||||
|
@ -153,6 +165,11 @@ function toDart() {
|
|||
var code = Blockly.Dart.workspaceToCode(workspace);
|
||||
setOutput(code);
|
||||
}
|
||||
|
||||
function toLua() {
|
||||
var code = Blockly.Lua.workspaceToCode(workspace);
|
||||
setOutput(code);
|
||||
}
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -287,6 +304,7 @@ h1 {
|
|||
<input type="button" value="Python" onclick="toPython()">
|
||||
<input type="button" value="PHP" onclick="toPhp()">
|
||||
<input type="button" value="Dart" onclick="toDart()">
|
||||
<input type="button" value="Lua" onclick="toLua()">
|
||||
</p>
|
||||
</td></tr><tr><td height="99%">
|
||||
<textarea id="importExport" readonly="readonly" wrap="off"></textarea>
|
||||
|
|
163
tests/generators/unittest_lua.js
Normal file
163
tests/generators/unittest_lua.js
Normal file
|
@ -0,0 +1,163 @@
|
|||
/**
|
||||
* @license
|
||||
* Visual Blocks Language
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* @fileoverview Generating Lua for unit test blocks.
|
||||
* @author rodrigoq@google.com (Rodrigo Queiro)
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
Blockly.Lua['unittest_main'] = function(block) {
|
||||
// Container for unit tests.
|
||||
var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults',
|
||||
Blockly.Variables.NAME_TYPE);
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'unittest_report',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '()',
|
||||
' -- Create test report.',
|
||||
' local report = {}',
|
||||
' local summary = {}',
|
||||
' local fails = 0',
|
||||
' for _, v in pairs(' + resultsVar + ') do',
|
||||
' if v["success"] then',
|
||||
' table.insert(summary, ".")',
|
||||
' else',
|
||||
' table.insert(summary, "F")',
|
||||
' fails = fails + 1',
|
||||
' table.insert(report, "FAIL: " .. v["title"])',
|
||||
' table.insert(report, v["log"])',
|
||||
' end',
|
||||
' end',
|
||||
' table.insert(report, 1, table.concat(summary))',
|
||||
' table.insert(report, "")',
|
||||
' table.insert(report, "Number of tests run: " .. #' + resultsVar + ')',
|
||||
' table.insert(report, "")',
|
||||
' if fails > 0 then',
|
||||
' table.insert(report, "FAILED (failures=" .. fails .. ")")',
|
||||
' else',
|
||||
' table.insert(report, "OK")',
|
||||
' end',
|
||||
' return table.concat(report, "\\n")',
|
||||
'end']);
|
||||
// Setup global to hold test results.
|
||||
var code = resultsVar + ' = {}\n';
|
||||
// Run tests (unindented).
|
||||
code += Blockly.Lua.statementToCode(block, 'DO')
|
||||
.replace(/^ /, '').replace(/\n /g, '\n');
|
||||
var reportVar = Blockly.Lua.variableDB_.getDistinctName(
|
||||
'report', Blockly.Variables.NAME_TYPE);
|
||||
code += reportVar + ' = ' + functionName + '()\n';
|
||||
// Destroy results.
|
||||
code += resultsVar + ' = nil\n';
|
||||
// Print the report.
|
||||
code += 'print(' + reportVar + ')\n';
|
||||
return code;
|
||||
};
|
||||
|
||||
Blockly.Lua['unittest_main'].defineAssert_ = function(block) {
|
||||
var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults',
|
||||
Blockly.Variables.NAME_TYPE);
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'assertEquals',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ +
|
||||
'(actual, expected, message)',
|
||||
' -- Asserts that a value equals another value.',
|
||||
' assert(' + resultsVar + ' ~= nil, ' +
|
||||
'"Orphaned assert equals: " .. message)',
|
||||
' if type(actual) == "table" and type(expected) == "table" then',
|
||||
' local lists_match = #actual == #expected',
|
||||
' if lists_match then',
|
||||
' for i, v1 in ipairs(actual) do',
|
||||
' local v2 = expected[i]',
|
||||
' if type(v1) == "number" and type(v2) == "number" then',
|
||||
' if math.abs(v1 - v2) > 1e-9 then',
|
||||
' lists_match = false',
|
||||
' end',
|
||||
' elseif v1 ~= v2 then',
|
||||
' lists_match = false',
|
||||
' end',
|
||||
' end',
|
||||
' end',
|
||||
' if lists_match then',
|
||||
' table.insert(' + resultsVar +
|
||||
', {success=true, log="OK", title=message})',
|
||||
' return',
|
||||
' else',
|
||||
' -- produce the non-matching strings for a human-readable error',
|
||||
' expected = "{" .. table.concat(expected, ", ") .. "}"',
|
||||
' actual = "{" .. table.concat(actual, ", ") .. "}"',
|
||||
' end',
|
||||
' end',
|
||||
' if actual == expected then',
|
||||
' table.insert(' + resultsVar +
|
||||
', {success=true, log="OK", title=message})',
|
||||
' else',
|
||||
' table.insert(' + resultsVar + ', {success=false, ' +
|
||||
'log=string.format("Expected: %s\\nActual: %s"' +
|
||||
', expected, actual), title=message})',
|
||||
' end',
|
||||
'end']);
|
||||
return functionName;
|
||||
};
|
||||
|
||||
Blockly.Lua['unittest_assertequals'] = function(block) {
|
||||
// Asserts that a value equals another value.
|
||||
var message = Blockly.Lua.quote_(block.getFieldValue('MESSAGE'));
|
||||
var actual = Blockly.Lua.valueToCode(block, 'ACTUAL',
|
||||
Blockly.Lua.ORDER_NONE) || 'nil';
|
||||
var expected = Blockly.Lua.valueToCode(block, 'EXPECTED',
|
||||
Blockly.Lua.ORDER_NONE) || 'nil';
|
||||
return Blockly.Lua['unittest_main'].defineAssert_() +
|
||||
'(' + actual + ', ' + expected + ', ' + message + ')\n';
|
||||
};
|
||||
|
||||
Blockly.Lua['unittest_assertvalue'] = function(block) {
|
||||
// Asserts that a value is true, false, or null.
|
||||
var message = Blockly.Lua.quote_(block.getFieldValue('MESSAGE'));
|
||||
var actual = Blockly.Lua.valueToCode(block, 'ACTUAL',
|
||||
Blockly.Lua.ORDER_NONE) || 'nil';
|
||||
var expected = block.getFieldValue('EXPECTED');
|
||||
if (expected == 'TRUE') {
|
||||
expected = 'true';
|
||||
} else if (expected == 'FALSE') {
|
||||
expected = 'false';
|
||||
} else if (expected == 'NULL') {
|
||||
expected = 'nil';
|
||||
}
|
||||
return Blockly.Lua.unittest_main.defineAssert_() +
|
||||
'(' + actual + ', ' + expected + ', ' + message + ')\n';
|
||||
};
|
||||
|
||||
Blockly.Lua['unittest_fail'] = function(block) {
|
||||
// Always assert an error.
|
||||
var resultsVar = Blockly.Lua.variableDB_.getName('unittestResults',
|
||||
Blockly.Variables.NAME_TYPE);
|
||||
var message = Blockly.Lua.quote_(block.getFieldValue('MESSAGE'));
|
||||
var functionName = Blockly.Lua.provideFunction_(
|
||||
'unittest_fail',
|
||||
['function ' + Blockly.Lua.FUNCTION_NAME_PLACEHOLDER_ + '(message)',
|
||||
' -- Always assert an error.',
|
||||
' assert(' + resultsVar +
|
||||
' ~= nil, "Orphaned assert fail: " .. message)',
|
||||
' table.insert(' + resultsVar +
|
||||
', {success=false, log="Fail.", title=message})',
|
||||
'end']);
|
||||
return functionName + '(' + message + ')\n';
|
||||
};
|
|
@ -51,4 +51,8 @@
|
|||
</block>
|
||||
</statement>
|
||||
</block>
|
||||
<block type="variables_get" x="300" y="100">
|
||||
<field name="VAR">naked</field>
|
||||
<comment pinned="true" h="80" w="160">Intentionally non-connected variable.</comment>
|
||||
</block>
|
||||
</xml>
|
||||
|
|
|
@ -39,23 +39,23 @@ function verify_DB_(msg, expected, db) {
|
|||
function test_DB_addConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
var o2 = {y_: 2, sourceBlock_: {}};
|
||||
db.addConnection_(o2);
|
||||
db.addConnection(o2);
|
||||
verify_DB_('Adding connection #2', [o2], db);
|
||||
|
||||
var o4 = {y_: 4, sourceBlock_: {}};
|
||||
db.addConnection_(o4);
|
||||
db.addConnection(o4);
|
||||
verify_DB_('Adding connection #4', [o2, o4], db);
|
||||
|
||||
var o1 = {y_: 1, sourceBlock_: {}};
|
||||
db.addConnection_(o1);
|
||||
db.addConnection(o1);
|
||||
verify_DB_('Adding connection #1', [o1, o2, o4], db);
|
||||
|
||||
var o3a = {y_: 3, sourceBlock_: {}};
|
||||
db.addConnection_(o3a);
|
||||
db.addConnection(o3a);
|
||||
verify_DB_('Adding connection #3a', [o1, o2, o3a, o4], db);
|
||||
|
||||
var o3b = {y_: 3, sourceBlock_: {}};
|
||||
db.addConnection_(o3b);
|
||||
db.addConnection(o3b);
|
||||
verify_DB_('Adding connection #3b', [o1, o2, o3b, o3a, o4], db);
|
||||
}
|
||||
|
||||
|
@ -67,12 +67,12 @@ function test_DB_removeConnection() {
|
|||
var o3b = {y_: 3, sourceBlock_: {}};
|
||||
var o3c = {y_: 3, sourceBlock_: {}};
|
||||
var o4 = {y_: 4, sourceBlock_: {}};
|
||||
db.addConnection_(o1);
|
||||
db.addConnection_(o2);
|
||||
db.addConnection_(o3c);
|
||||
db.addConnection_(o3b);
|
||||
db.addConnection_(o3a);
|
||||
db.addConnection_(o4);
|
||||
db.addConnection(o1);
|
||||
db.addConnection(o2);
|
||||
db.addConnection(o3c);
|
||||
db.addConnection(o3b);
|
||||
db.addConnection(o3a);
|
||||
db.addConnection(o4);
|
||||
verify_DB_('Adding connections 1-4', [o1, o2, o3a, o3b, o3c, o4], db);
|
||||
|
||||
db.removeConnection_(o2);
|
||||
|
@ -93,3 +93,190 @@ function test_DB_removeConnection() {
|
|||
db.removeConnection_(o3b);
|
||||
verify_DB_('Removing connection #3b', [], db);
|
||||
}
|
||||
|
||||
function test_DB_getNeighbours() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(helper_getNeighbours(db,
|
||||
10 /* x */, 10 /* y */, 100 /* radius */).length, 0);
|
||||
|
||||
// Set up some connections.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(0, i,
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
// Test block belongs at beginning.
|
||||
var result = helper_getNeighbours(db, 0, 0, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at middle.
|
||||
result = helper_getNeighbours(db, 0, 4, 2);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 2]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at end.
|
||||
result = helper_getNeighbours(db, 0, 9, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 5]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block has no neighbours due to being out of range in the x direction.
|
||||
result = helper_getNeighbours(db, 10, 9, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range in the y direction.
|
||||
result = helper_getNeighbours(db, 0, 19, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range diagonally.
|
||||
result = helper_getNeighbours(db, -2, -2, 2);
|
||||
assertEquals(result.length, 0);
|
||||
}
|
||||
|
||||
function test_DB_findPositionForConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
db.addConnection(helper_createConnection(0, 0, Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, 1, Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, 2, Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, 4, Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, 5, Blockly.PREVIOUS_STATEMENT));
|
||||
|
||||
assertEquals(5, db.length);
|
||||
var conn = helper_createConnection(0, 3, Blockly.PREVIOUS_STATEMENT);
|
||||
assertEquals(3, db.findPositionForConnection_(conn));
|
||||
}
|
||||
|
||||
function test_DB_findConnection() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(i, 0,
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
db.addConnection(helper_createConnection(0, i,
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
var conn = helper_createConnection(3, 3, Blockly.PREVIOUS_STATEMENT);
|
||||
db.addConnection(conn);
|
||||
assertEquals(conn, db[db.findConnection(conn)]);
|
||||
|
||||
conn = helper_createConnection(3, 3, Blockly.PREVIOUS_STATEMENT);
|
||||
assertEquals(-1, db.findConnection(conn));
|
||||
}
|
||||
|
||||
function test_DB_ordering() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection(helper_createConnection(0, 9 - i,
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
for (i = 0; i < 10; i++) {
|
||||
assertEquals(i, db[i].y_);
|
||||
}
|
||||
|
||||
// quasi-random
|
||||
var xCoords = [-29, -47, -77, 2, 43, 34, -59, -52, -90, -36, -91, 38, 87, -20,
|
||||
60, 4, -57, 65, -37, -81, 57, 58, -96, 1, 67, -79, 34, 93, -90, -99, -62,
|
||||
4, 11, -36, -51, -72, 3, -50, -24, -45, -92, -38, 37, 24, -47, -73, 79,
|
||||
-20, 99, 43, -10, -87, 19, 35, -62, -36, 49, 86, -24, -47, -89, 33, -44,
|
||||
25, -73, -91, 85, 6, 0, 89, -94, 36, -35, 84, -9, 96, -21, 52, 10, -95, 7,
|
||||
-67, -70, 62, 9, -40, -95, -9, -94, 55, 57, -96, 55, 8, -48, -57, -87, 81,
|
||||
23, 65];
|
||||
var yCoords = [-81, 82, 5, 47, 30, 57, -12, 28, 38, 92, -25, -20, 23, -51, 73,
|
||||
-90, 8, 28, -51, -15, 81, -60, -6, -16, 77, -62, -42, -24, 35, 95, -46,
|
||||
-7, 61, -16, 14, 91, 57, -38, 27, -39, 92, 47, -98, 11, -33, -72, 64, 38,
|
||||
-64, -88, -35, -59, -76, -94, 45, -25, -100, -95, 63, -97, 45, 98, 99, 34,
|
||||
27, 52, -18, -45, 66, -32, -38, 70, -73, -23, 5, -2, -13, -9, 48, 74, -97,
|
||||
-11, 35, -79, -16, -77, 83, -57, -53, 35, -44, 100, -27, -15, 5, 39, 33,
|
||||
-19, -20, -95];
|
||||
for (i = 0; i < xCoords.length; i++) {
|
||||
db.addConnection(helper_createConnection(xCoords[i], yCoords[i],
|
||||
Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
for (i = 1; i < xCoords.length; i++) {
|
||||
assertTrue(db[i].y_ >= db[i - 1].y_);
|
||||
}
|
||||
}
|
||||
|
||||
function test_SearchForClosest() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
var sharedWorkspace = {id: "Shared workspace"};
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(null, helper_searchDB(db, 10 /* x */, 10 /* y */,
|
||||
100 /* radius */));
|
||||
|
||||
db.addConnection(helper_createConnection(100, 0, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace));
|
||||
assertEquals(null, helper_searchDB(db, 0, 0, 5, sharedWorkspace));
|
||||
|
||||
db = new Blockly.ConnectionDB();
|
||||
for (var i = 0; i < 10; i++) {
|
||||
var tempConn = helper_createConnection(0, i, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
}
|
||||
|
||||
// Should be at 0, 9.
|
||||
var last = db[db.length - 1];
|
||||
// Correct connection is last in db; many connections in radius.
|
||||
assertEquals(last, helper_searchDB(db, 0, 10, 15, sharedWorkspace));
|
||||
// Nothing nearby.
|
||||
assertEquals(null, helper_searchDB(db, 100, 100, 3, sharedWorkspace));
|
||||
// First in db, exact match.
|
||||
assertEquals(db[0], helper_searchDB(db, 0, 0, 0, sharedWorkspace));
|
||||
|
||||
tempConn = helper_createConnection(6, 6, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
tempConn = helper_createConnection(5, 5, Blockly.PREVIOUS_STATEMENT,
|
||||
sharedWorkspace);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
db.addConnection(tempConn);
|
||||
|
||||
var result = helper_searchDB(db, 4, 6, 3, sharedWorkspace);
|
||||
assertEquals(5, result.x_);
|
||||
assertEquals(5, result.y_);
|
||||
}
|
||||
|
||||
|
||||
function helper_getNeighbours(db, x, y, radius) {
|
||||
return db.getNeighbours(helper_createConnection(x, y, Blockly.NEXT_STATEMENT),
|
||||
radius);
|
||||
}
|
||||
|
||||
function helper_searchDB(db, x, y, radius, shared_workspace) {
|
||||
var tempConn = helper_createConnection(x, y,
|
||||
Blockly.NEXT_STATEMENT, shared_workspace);
|
||||
tempConn.sourceBlock_ = helper_makeSourceBlock(shared_workspace);
|
||||
return db.searchForClosest(tempConn, radius, 0, 0);
|
||||
}
|
||||
|
||||
function helper_makeSourceBlock(sharedWorkspace) {
|
||||
return {workspace: sharedWorkspace,
|
||||
parentBlock_: null,
|
||||
getParent: function() { return null; },
|
||||
movable_: true,
|
||||
isMovable: function() { return true; },
|
||||
isShadow: function() { return false; }
|
||||
};
|
||||
}
|
||||
|
||||
function helper_createConnection(x, y, type, opt_shared_workspace) {
|
||||
var workspace = opt_shared_workspace ? opt_shared_workspace : {};
|
||||
var conn = new Blockly.Connection({workspace: workspace}, type);
|
||||
conn.x_ = x;
|
||||
conn.y_ = y;
|
||||
return conn;
|
||||
}
|
|
@ -33,10 +33,14 @@ var dummyWorkspace;
|
|||
|
||||
function connectionTest_setUp() {
|
||||
dummyWorkspace = {};
|
||||
input = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.INPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
|
||||
previous = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.PREVIOUS_STATEMENT);
|
||||
next = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.NEXT_STATEMENT);
|
||||
input = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.INPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.OUTPUT_VALUE);
|
||||
previous = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.PREVIOUS_STATEMENT);
|
||||
next = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.NEXT_STATEMENT);
|
||||
}
|
||||
|
||||
function connectionTest_tearDown() {
|
||||
|
@ -48,13 +52,14 @@ function connectionTest_tearDown() {
|
|||
}
|
||||
|
||||
/**
|
||||
* These tests check that the reasons for failures to connect are consistent (internal view of
|
||||
* error states).
|
||||
* These tests check that the reasons for failures to connect are consistent
|
||||
* (internal view of error states).
|
||||
*/
|
||||
function testCanConnectWithReason_TargetNull() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_TARGET_NULL, input.canConnectWithReason_(null));
|
||||
assertEquals(Blockly.Connection.REASON_TARGET_NULL,
|
||||
input.canConnectWithReason_(null));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
@ -62,9 +67,11 @@ function testCanConnectWithReason_TargetNull() {
|
|||
function testCanConnectWithReason_Disconnect() {
|
||||
connectionTest_setUp();
|
||||
|
||||
var tempConnection = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
|
||||
var tempConnection = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.OUTPUT_VALUE);
|
||||
Blockly.Connection.connectReciprocally(input, tempConnection);
|
||||
assertEquals(Blockly.Connection.REASON_MUST_DISCONNECT, input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.REASON_MUST_DISCONNECT,
|
||||
input.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
@ -73,9 +80,11 @@ function testCanConnectWithReason_DifferentWorkspaces() {
|
|||
connectionTest_setUp();
|
||||
|
||||
input = new Blockly.Connection({workspace: {}}, Blockly.INPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace}, Blockly.OUTPUT_VALUE);
|
||||
output = new Blockly.Connection({workspace: dummyWorkspace},
|
||||
Blockly.OUTPUT_VALUE);
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_DIFFERENT_WORKSPACES, input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.REASON_DIFFERENT_WORKSPACES,
|
||||
input.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
@ -86,7 +95,8 @@ function testCanConnectWithReason_Self() {
|
|||
|
||||
var block = {type_: "test block"};
|
||||
input.sourceBlock_ = block;
|
||||
assertEquals(Blockly.Connection.REASON_SELF_CONNECTION, input.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_SELF_CONNECTION,
|
||||
input.canConnectWithReason_(input));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
@ -94,17 +104,25 @@ function testCanConnectWithReason_Self() {
|
|||
function testCanConnectWithReason_Type() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, input.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, input.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
input.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
input.canConnectWithReason_(next));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, output.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, output.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
output.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
output.canConnectWithReason_(next));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, previous.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, previous.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
previous.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
previous.canConnectWithReason_(output));
|
||||
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, next.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE, next.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
next.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.REASON_WRONG_TYPE,
|
||||
next.canConnectWithReason_(output));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
@ -112,17 +130,21 @@ function testCanConnectWithReason_Type() {
|
|||
function testCanConnectWithReason_CanConnect() {
|
||||
connectionTest_setUp();
|
||||
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT, previous.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT, next.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT, input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT, output.canConnectWithReason_(input));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
previous.canConnectWithReason_(next));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
next.canConnectWithReason_(previous));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
input.canConnectWithReason_(output));
|
||||
assertEquals(Blockly.Connection.CAN_CONNECT,
|
||||
output.canConnectWithReason_(input));
|
||||
|
||||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* The next set of tests checks that exceptions are being thrown at the correct times (external
|
||||
* view of errors).
|
||||
* The next set of tests checks that exceptions are being thrown at the correct
|
||||
* times (external view of errors).
|
||||
*/
|
||||
function testCheckConnection_Self() {
|
||||
connectionTest_setUp();
|
||||
|
@ -222,6 +244,40 @@ function testCheckConnection_TypeNextOutput() {
|
|||
connectionTest_tearDown();
|
||||
}
|
||||
|
||||
function test_isConnectionAllowed() {
|
||||
var sharedWorkspace = {};
|
||||
// Two connections of opposite types near each other.
|
||||
var one = helper_createConnection(5 /* x */, 10 /* y */,
|
||||
Blockly.INPUT_VALUE);
|
||||
one.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
var two = helper_createConnection(10 /* x */, 15 /* y */,
|
||||
Blockly.OUTPUT_VALUE);
|
||||
two.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
assertTrue(one.isConnectionAllowed(two, 20.0));
|
||||
// Move connections farther apart.
|
||||
two.x_ = 100;
|
||||
two.y_ = 100;
|
||||
assertFalse(one.isConnectionAllowed(two, 20.0));
|
||||
|
||||
// Don't offer to connect an already connected left (male) value plug to
|
||||
// an available right (female) value plug.
|
||||
var three = helper_createConnection(0, 0, Blockly.OUTPUT_VALUE);
|
||||
three.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
assertTrue(one.isConnectionAllowed(three, 20.0));
|
||||
var four = helper_createConnection(0, 0, Blockly.INPUT_VALUE);
|
||||
four.sourceBlock_ = helper_makeSourceBlock(sharedWorkspace);
|
||||
|
||||
Blockly.Connection.connectReciprocally(three, four);
|
||||
assertFalse(one.isConnectionAllowed(three, 20.0));
|
||||
|
||||
// Don't connect two connections on the same block.
|
||||
two.sourceBlock_ = one.sourceBlock_;
|
||||
assertFalse(one.isConnectionAllowed(two, 1000.0));
|
||||
}
|
||||
|
||||
function testCheckConnection_Okay() {
|
||||
connectionTest_setUp();
|
||||
previous.checkConnection_(next);
|
||||
|
|
76
tests/jsunit/db_test.js
Normal file
76
tests/jsunit/db_test.js
Normal file
|
@ -0,0 +1,76 @@
|
|||
/**
|
||||
* @license
|
||||
* Blockly Tests
|
||||
*
|
||||
* Copyright 2016 Google Inc.
|
||||
* https://developers.google.com/blockly/
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
function test_DB_getNeighbours() {
|
||||
var db = new Blockly.ConnectionDB();
|
||||
|
||||
// Search an empty list.
|
||||
assertEquals(helper_getNeighbours(db, 10 /* x */, 10 /* y */, 100 /* radius */).length, 0);
|
||||
|
||||
// Set up some connections.
|
||||
for (var i = 0; i < 10; i++) {
|
||||
db.addConnection_(helper_createConnection(0, i, Blockly.PREVIOUS_STATEMENT));
|
||||
}
|
||||
|
||||
// Test block belongs at beginning
|
||||
var result = helper_getNeighbours(db, 0, 0, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at middle
|
||||
result = helper_getNeighbours(db, 0, 4, 2);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 2]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block belongs at end
|
||||
result = helper_getNeighbours(db, 0, 9, 4);
|
||||
assertEquals(5, result.length);
|
||||
for (i = 0; i < result.length; i++) {
|
||||
assertNotEquals(result.indexOf(db[i + 5]), -1); // contains
|
||||
}
|
||||
|
||||
// Test block has no neighbours due to being out of range in the x direction
|
||||
result = helper_getNeighbours(db, 10, 9, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range in the y direction
|
||||
result = helper_getNeighbours(db, 0, 19, 4);
|
||||
assertEquals(result.length, 0);
|
||||
|
||||
// Test block has no neighbours due to being out of range diagonally
|
||||
result = helper_getNeighbours(db, -2, -2, 2);
|
||||
assertEquals(result.length, 0);
|
||||
}
|
||||
|
||||
function helper_getNeighbours(db, x, y, radius) {
|
||||
return db.getNeighbours(helper_createConnection(x, y, Blockly.NEXT_STATEMENT), radius);
|
||||
}
|
||||
|
||||
function helper_createConnection(x, y, type) {
|
||||
var conn = new Blockly.Connection({workspace: {}}, type);
|
||||
conn.x_ = x;
|
||||
conn.y_ = y;
|
||||
return conn;
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue