/** * @license * Visual Blocks Editor * * Copyright 2012 Google Inc. * https://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() .appendField(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') .appendField(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.getFieldValue('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.interpolateMsg('%1 %2', ['OP', new Blockly.FieldDropdown(OPERATORS)], ['NUM', 'Number', Blockly.ALIGN_RIGHT], Blockly.ALIGN_RIGHT); // Assign 'this' to a variable for use in the tooltip closure below. var thisBlock = this; this.setTooltip(function() { var mode = thisBlock.getFieldValue('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') .appendField(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.getFieldValue('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() .appendField(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() .appendField(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.getFieldValue('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.interpolateMsg( // TODO: Combine these messages instead of using concatenation. Blockly.Msg.MATH_CHANGE_TITLE_CHANGE + ' %1 ' + Blockly.Msg.MATH_CHANGE_INPUT_BY + ' %2', ['VAR', new Blockly.FieldVariable(Blockly.Msg.MATH_CHANGE_TITLE_ITEM)], ['DELTA', 'Number', Blockly.ALIGN_RIGHT], Blockly.ALIGN_RIGHT); 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.getFieldValue('VAR')); }); }, getVars: function() { return [this.getFieldValue('VAR')]; }, renameVar: function(oldName, newName) { if (Blockly.Names.equals(oldName, this.getFieldValue('VAR'))) { this.setFieldValue(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') .appendField(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') .appendField(dropdown, 'OP'); this.setTooltip(function() { var mode = thisBlock.getFieldValue('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() .appendField(Blockly.Msg.MATH_RANDOM_FLOAT_TITLE_RANDOM); this.setTooltip(Blockly.Msg.MATH_RANDOM_FLOAT_TOOLTIP); } };