mirror of
https://github.com/google/blockly.git
synced 2026-01-08 01:20:12 +01:00
365 lines
13 KiB
JavaScript
365 lines
13 KiB
JavaScript
/**
|
|
* Visual Blocks Editor
|
|
*
|
|
* Copyright 2012 Google Inc.
|
|
* http://blockly.googlecode.com/
|
|
*
|
|
* 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 Math blocks for Blockly.
|
|
* @author q.neutron@gmail.com (Quynh Neutron)
|
|
*/
|
|
'use strict';
|
|
|
|
goog.provide('Blockly.Blocks.math');
|
|
|
|
goog.require('Blockly.Blocks');
|
|
|
|
|
|
Blockly.Blocks['math_number'] = {
|
|
// Numeric value.
|
|
init: function() {
|
|
this.setHelpUrl(Blockly.Msg.MATH_NUMBER_HELPURL);
|
|
this.setColour(230);
|
|
this.appendDummyInput()
|
|
.appendTitle(new Blockly.FieldTextInput('0',
|
|
Blockly.FieldTextInput.numberValidator), 'NUM');
|
|
this.setOutput(true, 'Number');
|
|
this.setTooltip(Blockly.Msg.MATH_NUMBER_TOOLTIP);
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_arithmetic'] = {
|
|
// Basic arithmetic operator.
|
|
init: function() {
|
|
var OPERATORS =
|
|
[[Blockly.Msg.MATH_ADDITION_SYMBOL, 'ADD'],
|
|
[Blockly.Msg.MATH_SUBTRACTION_SYMBOL, 'MINUS'],
|
|
[Blockly.Msg.MATH_MULTIPLICATION_SYMBOL, 'MULTIPLY'],
|
|
[Blockly.Msg.MATH_DIVISION_SYMBOL, 'DIVIDE'],
|
|
[Blockly.Msg.MATH_POWER_SYMBOL, 'POWER']];
|
|
this.setHelpUrl(Blockly.Msg.MATH_ARITHMETIC_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.appendValueInput('A')
|
|
.setCheck('Number');
|
|
this.appendValueInput('B')
|
|
.setCheck('Number')
|
|
.appendTitle(new Blockly.FieldDropdown(OPERATORS), 'OP');
|
|
this.setInputsInline(true);
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
var mode = thisBlock.getTitleValue('OP');
|
|
var TOOLTIPS = {
|
|
ADD: Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_ADD,
|
|
MINUS: Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MINUS,
|
|
MULTIPLY: Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_MULTIPLY,
|
|
DIVIDE: Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_DIVIDE,
|
|
POWER: Blockly.Msg.MATH_ARITHMETIC_TOOLTIP_POWER
|
|
};
|
|
return TOOLTIPS[mode];
|
|
});
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_single'] = {
|
|
// Advanced math operators with single operand.
|
|
init: function() {
|
|
var OPERATORS =
|
|
[[Blockly.Msg.MATH_SINGLE_OP_ROOT, 'ROOT'],
|
|
[Blockly.Msg.MATH_SINGLE_OP_ABSOLUTE, 'ABS'],
|
|
['-', 'NEG'],
|
|
['ln', 'LN'],
|
|
['log10', 'LOG10'],
|
|
['e^', 'EXP'],
|
|
['10^', 'POW10']];
|
|
this.setHelpUrl(Blockly.Msg.MATH_SINGLE_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.appendValueInput('NUM')
|
|
.setCheck('Number')
|
|
.appendTitle(new Blockly.FieldDropdown(OPERATORS), 'OP');
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
var mode = thisBlock.getTitleValue('OP');
|
|
var TOOLTIPS = {
|
|
ROOT: Blockly.Msg.MATH_SINGLE_TOOLTIP_ROOT,
|
|
ABS: Blockly.Msg.MATH_SINGLE_TOOLTIP_ABS,
|
|
NEG: Blockly.Msg.MATH_SINGLE_TOOLTIP_NEG,
|
|
LN: Blockly.Msg.MATH_SINGLE_TOOLTIP_LN,
|
|
LOG10: Blockly.Msg.MATH_SINGLE_TOOLTIP_LOG10,
|
|
EXP: Blockly.Msg.MATH_SINGLE_TOOLTIP_EXP,
|
|
POW10: Blockly.Msg.MATH_SINGLE_TOOLTIP_POW10
|
|
};
|
|
return TOOLTIPS[mode];
|
|
});
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_trig'] = {
|
|
// Trigonometry operators.
|
|
init: function() {
|
|
var OPERATORS =
|
|
[[Blockly.Msg.MATH_TRIG_SIN, 'SIN'],
|
|
[Blockly.Msg.MATH_TRIG_COS, 'COS'],
|
|
[Blockly.Msg.MATH_TRIG_TAN, 'TAN'],
|
|
[Blockly.Msg.MATH_TRIG_ASIN, 'ASIN'],
|
|
[Blockly.Msg.MATH_TRIG_ACOS, 'ACOS'],
|
|
[Blockly.Msg.MATH_TRIG_ATAN, 'ATAN']];
|
|
this.setHelpUrl(Blockly.Msg.MATH_TRIG_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.appendValueInput('NUM')
|
|
.setCheck('Number')
|
|
.appendTitle(new Blockly.FieldDropdown(OPERATORS), 'OP');
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
var mode = thisBlock.getTitleValue('OP');
|
|
var TOOLTIPS = {
|
|
SIN: Blockly.Msg.MATH_TRIG_TOOLTIP_SIN,
|
|
COS: Blockly.Msg.MATH_TRIG_TOOLTIP_COS,
|
|
TAN: Blockly.Msg.MATH_TRIG_TOOLTIP_TAN,
|
|
ASIN: Blockly.Msg.MATH_TRIG_TOOLTIP_ASIN,
|
|
ACOS: Blockly.Msg.MATH_TRIG_TOOLTIP_ACOS,
|
|
ATAN: Blockly.Msg.MATH_TRIG_TOOLTIP_ATAN
|
|
};
|
|
return TOOLTIPS[mode];
|
|
});
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_constant'] = {
|
|
// Constants: PI, E, the Golden Ratio, sqrt(2), 1/sqrt(2), INFINITY.
|
|
init: function() {
|
|
var CONSTANTS =
|
|
[['\u03c0', 'PI'],
|
|
['e', 'E'],
|
|
['\u03c6', 'GOLDEN_RATIO'],
|
|
['sqrt(2)', 'SQRT2'],
|
|
['sqrt(\u00bd)', 'SQRT1_2'],
|
|
['\u221e', 'INFINITY']];
|
|
this.setHelpUrl(Blockly.Msg.MATH_CONSTANT_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.appendDummyInput()
|
|
.appendTitle(new Blockly.FieldDropdown(CONSTANTS), 'CONSTANT');
|
|
this.setTooltip(Blockly.Msg.MATH_CONSTANT_TOOLTIP);
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_number_property'] = {
|
|
// Check if a number is even, odd, prime, whole, positive, or negative
|
|
// or if it is divisible by certain number. Returns true or false.
|
|
init: function() {
|
|
var PROPERTIES =
|
|
[[Blockly.Msg.MATH_IS_EVEN, 'EVEN'],
|
|
[Blockly.Msg.MATH_IS_ODD, 'ODD'],
|
|
[Blockly.Msg.MATH_IS_PRIME, 'PRIME'],
|
|
[Blockly.Msg.MATH_IS_WHOLE, 'WHOLE'],
|
|
[Blockly.Msg.MATH_IS_POSITIVE, 'POSITIVE'],
|
|
[Blockly.Msg.MATH_IS_NEGATIVE, 'NEGATIVE'],
|
|
[Blockly.Msg.MATH_IS_DIVISIBLE_BY, 'DIVISIBLE_BY']];
|
|
this.setColour(230);
|
|
this.appendValueInput('NUMBER_TO_CHECK')
|
|
.setCheck('Number');
|
|
var dropdown = new Blockly.FieldDropdown(PROPERTIES, function(option) {
|
|
var divisorInput = (option == 'DIVISIBLE_BY');
|
|
this.sourceBlock_.updateShape(divisorInput);
|
|
});
|
|
this.appendDummyInput()
|
|
.appendTitle(dropdown, 'PROPERTY');
|
|
this.setInputsInline(true);
|
|
this.setOutput(true, 'Boolean');
|
|
this.setTooltip(Blockly.Msg.MATH_IS_TOOLTIP);
|
|
},
|
|
mutationToDom: function() {
|
|
// Save whether the 'divisorInput' should be true of false (present or not).
|
|
var container = document.createElement('mutation');
|
|
var divisorInput = (this.getTitleValue('PROPERTY') == 'DIVISIBLE_BY');
|
|
container.setAttribute('divisor_input', divisorInput);
|
|
return container;
|
|
},
|
|
domToMutation: function(xmlElement) {
|
|
// Restore the 'divisorInput'.
|
|
var divisorInput = (xmlElement.getAttribute('divisor_input') == 'true');
|
|
this.updateShape(divisorInput);
|
|
},
|
|
updateShape: function(divisorInput) {
|
|
// Add or remove a Value Input.
|
|
var inputExists = this.getInput('DIVISOR');
|
|
if (divisorInput) {
|
|
if (!inputExists) {
|
|
this.appendValueInput('DIVISOR')
|
|
.setCheck('Number');
|
|
}
|
|
} else if (inputExists) {
|
|
this.removeInput('DIVISOR');
|
|
}
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_change'] = {
|
|
// Add to a variable in place.
|
|
init: function() {
|
|
this.setHelpUrl(Blockly.Msg.MATH_CHANGE_HELPURL);
|
|
this.setColour(230);
|
|
this.appendValueInput('DELTA')
|
|
.setCheck('Number')
|
|
.appendTitle(Blockly.Msg.MATH_CHANGE_TITLE_CHANGE)
|
|
.appendTitle(new Blockly.FieldVariable(
|
|
Blockly.Msg.MATH_CHANGE_TITLE_ITEM), 'VAR')
|
|
.appendTitle(Blockly.Msg.MATH_CHANGE_INPUT_BY);
|
|
this.setPreviousStatement(true);
|
|
this.setNextStatement(true);
|
|
// Assign 'this' to a variable for use in the tooltip closure below.
|
|
var thisBlock = this;
|
|
this.setTooltip(function() {
|
|
return Blockly.Msg.MATH_CHANGE_TOOLTIP.replace('%1',
|
|
thisBlock.getTitleValue('VAR'));
|
|
});
|
|
},
|
|
getVars: function() {
|
|
return [this.getTitleValue('VAR')];
|
|
},
|
|
renameVar: function(oldName, newName) {
|
|
if (Blockly.Names.equals(oldName, this.getTitleValue('VAR'))) {
|
|
this.setTitleValue(newName, 'VAR');
|
|
}
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_round'] = {
|
|
// Rounding functions.
|
|
init: function() {
|
|
var OPERATORS =
|
|
[[Blockly.Msg.MATH_ROUND_OPERATOR_ROUND, 'ROUND'],
|
|
[Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDUP, 'ROUNDUP'],
|
|
[Blockly.Msg.MATH_ROUND_OPERATOR_ROUNDDOWN, 'ROUNDDOWN']];
|
|
this.setHelpUrl(Blockly.Msg.MATH_ROUND_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.appendValueInput('NUM')
|
|
.setCheck('Number')
|
|
.appendTitle(new Blockly.FieldDropdown(OPERATORS), 'OP');
|
|
this.setTooltip(Blockly.Msg.MATH_ROUND_TOOLTIP);
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_on_list'] = {
|
|
// Evaluate a list of numbers to return sum, average, min, max, etc.
|
|
// Some functions also work on text (min, max, mode, median).
|
|
init: function() {
|
|
var OPERATORS =
|
|
[[Blockly.Msg.MATH_ONLIST_OPERATOR_SUM, 'SUM'],
|
|
[Blockly.Msg.MATH_ONLIST_OPERATOR_MIN, 'MIN'],
|
|
[Blockly.Msg.MATH_ONLIST_OPERATOR_MAX, 'MAX'],
|
|
[Blockly.Msg.MATH_ONLIST_OPERATOR_AVERAGE, 'AVERAGE'],
|
|
[Blockly.Msg.MATH_ONLIST_OPERATOR_MEDIAN, 'MEDIAN'],
|
|
[Blockly.Msg.MATH_ONLIST_OPERATOR_MODE, 'MODE'],
|
|
[Blockly.Msg.MATH_ONLIST_OPERATOR_STD_DEV, 'STD_DEV'],
|
|
[Blockly.Msg.MATH_ONLIST_OPERATOR_RANDOM, 'RANDOM']];
|
|
// Assign 'this' to a variable for use in the closure below.
|
|
var thisBlock = this;
|
|
this.setHelpUrl(Blockly.Msg.MATH_ONLIST_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
var dropdown = new Blockly.FieldDropdown(OPERATORS, function(newOp) {
|
|
if (newOp == 'MODE') {
|
|
thisBlock.outputConnection.setCheck('Array');
|
|
} else {
|
|
thisBlock.outputConnection.setCheck('Number');
|
|
}
|
|
});
|
|
this.appendValueInput('LIST')
|
|
.setCheck('Array')
|
|
.appendTitle(dropdown, 'OP');
|
|
this.setTooltip(function() {
|
|
var mode = thisBlock.getTitleValue('OP');
|
|
var TOOLTIPS = {
|
|
SUM: Blockly.Msg.MATH_ONLIST_TOOLTIP_SUM,
|
|
MIN: Blockly.Msg.MATH_ONLIST_TOOLTIP_MIN,
|
|
MAX: Blockly.Msg.MATH_ONLIST_TOOLTIP_MAX,
|
|
AVERAGE: Blockly.Msg.MATH_ONLIST_TOOLTIP_AVERAGE,
|
|
MEDIAN: Blockly.Msg.MATH_ONLIST_TOOLTIP_MEDIAN,
|
|
MODE: Blockly.Msg.MATH_ONLIST_TOOLTIP_MODE,
|
|
STD_DEV: Blockly.Msg.MATH_ONLIST_TOOLTIP_STD_DEV,
|
|
RANDOM: Blockly.Msg.MATH_ONLIST_TOOLTIP_RANDOM
|
|
};
|
|
return TOOLTIPS[mode];
|
|
});
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_modulo'] = {
|
|
// Remainder of a division.
|
|
init: function() {
|
|
this.setHelpUrl(Blockly.Msg.MATH_MODULO_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.interpolateMsg(Blockly.Msg.MATH_MODULO_TITLE,
|
|
['DIVIDEND', 'Number', Blockly.ALIGN_RIGHT],
|
|
['DIVISOR', 'Number', Blockly.ALIGN_RIGHT],
|
|
Blockly.ALIGN_RIGHT);
|
|
this.setInputsInline(true);
|
|
this.setTooltip(Blockly.Msg.MATH_MODULO_TOOLTIP);
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_constrain'] = {
|
|
// Constrain a number between two limits.
|
|
init: function() {
|
|
this.setHelpUrl(Blockly.Msg.MATH_CONSTRAIN_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.interpolateMsg(Blockly.Msg.MATH_CONSTRAIN_TITLE,
|
|
['VALUE', 'Number', Blockly.ALIGN_RIGHT],
|
|
['LOW', 'Number', Blockly.ALIGN_RIGHT],
|
|
['HIGH', 'Number', Blockly.ALIGN_RIGHT],
|
|
Blockly.ALIGN_RIGHT)
|
|
this.setInputsInline(true);
|
|
this.setTooltip(Blockly.Msg.MATH_CONSTRAIN_TOOLTIP);
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_random_int'] = {
|
|
// Random integer between [X] and [Y].
|
|
init: function() {
|
|
this.setHelpUrl(Blockly.Msg.MATH_RANDOM_INT_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.interpolateMsg(Blockly.Msg.MATH_RANDOM_INT_TITLE,
|
|
['FROM', 'Number', Blockly.ALIGN_RIGHT],
|
|
['TO', 'Number', Blockly.ALIGN_RIGHT],
|
|
Blockly.ALIGN_RIGHT);
|
|
this.setInputsInline(true);
|
|
this.setTooltip(Blockly.Msg.MATH_RANDOM_INT_TOOLTIP);
|
|
}
|
|
};
|
|
|
|
Blockly.Blocks['math_random_float'] = {
|
|
// Random fraction between 0 and 1.
|
|
init: function() {
|
|
this.setHelpUrl(Blockly.Msg.MATH_RANDOM_FLOAT_HELPURL);
|
|
this.setColour(230);
|
|
this.setOutput(true, 'Number');
|
|
this.appendDummyInput()
|
|
.appendTitle(Blockly.Msg.MATH_RANDOM_FLOAT_TITLE_RANDOM);
|
|
this.setTooltip(Blockly.Msg.MATH_RANDOM_FLOAT_TOOLTIP);
|
|
}
|
|
};
|