From a5bb982044b6262fb95d8fcd95bd8c60464c2e19 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:08:29 +0800 Subject: [PATCH 01/74] Import Blocks from scratch-blocks branch --- .../Scratch/Advanced/Functions/index.js | 5 + .../Functions/procedures_callnoreturn.js | 335 ++++++++++++++++++ .../Functions/procedures_callreturn.js | 38 ++ .../Functions/procedures_defnoreturn.js | 335 ++++++++++++++++++ .../Functions/procedures_defreturn.js | 80 +++++ .../Advanced/Functions/procedures_ifreturn.js | 132 +++++++ .../blocks/Scratch/Advanced/List/index.js | 11 + .../Advanced/List/lists_create_with.js | 85 +++++ .../Scratch/Advanced/List/lists_getIndex.js | 166 +++++++++ .../Scratch/Advanced/List/lists_getSublist.js | 125 +++++++ .../Scratch/Advanced/List/lists_indexOf.js | 43 +++ .../Scratch/Advanced/List/lists_isEmpty.js | 27 ++ .../Scratch/Advanced/List/lists_length.js | 27 ++ .../Scratch/Advanced/List/lists_repeat.js | 51 +++ .../Scratch/Advanced/List/lists_setIndex.js | 132 +++++++ .../Scratch/Advanced/List/lists_sort.js | 62 ++++ .../Scratch/Advanced/List/lists_split.js | 75 ++++ .../Scratch/Advanced/List/lists_statement.js | 51 +++ .../Loops/controls_flow_statements.js | 33 ++ .../Scratch/Advanced/Loops/controls_for.js | 105 ++++++ .../Advanced/Loops/controls_forEach.js | 60 ++++ .../Scratch/Advanced/Loops/controls_repeat.js | 31 ++ .../Advanced/Loops/controls_repeat_ext.js | 57 +++ .../Advanced/Loops/controls_whileUntil.js | 50 +++ .../blocks/Scratch/Advanced/Loops/index.js | 6 + .../blocks/Scratch/Advanced/Variable/index.js | 2 + .../Advanced/Variable/variables_get.js | 28 ++ .../Advanced/Variable/variables_set.js | 35 ++ .../Binary/After Purchase/after_purchase.js | 64 ++++ .../Binary/After Purchase/check_result.js | 46 +++ .../Scratch/Binary/After Purchase/index.js | 4 + .../Binary/After Purchase/read_details.js | 45 +++ .../Binary/After Purchase/trade_again.js | 34 ++ .../Binary/Before Purchase/ask_price.js | 46 +++ .../Binary/Before Purchase/before_purchase.js | 61 ++++ .../Scratch/Binary/Before Purchase/index.js | 4 + .../Scratch/Binary/Before Purchase/payout.js | 45 +++ .../Binary/Before Purchase/purchase.js | 70 ++++ .../Binary/During Purchase/check_sell.js | 36 ++ .../Binary/During Purchase/during_purchase.js | 63 ++++ .../Scratch/Binary/During Purchase/index.js | 4 + .../Binary/During Purchase/sell_at_market.js | 33 ++ .../Binary/During Purchase/sell_price.js | 35 ++ .../Indicators/Parts/fast_ema_period.js | 28 ++ .../Scratch/Binary/Indicators/Parts/index.js | 7 + .../Binary/Indicators/Parts/input_list.js | 52 +++ .../Scratch/Binary/Indicators/Parts/period.js | 38 ++ .../Indicators/Parts/signal_ema_period.js | 28 ++ .../Indicators/Parts/slow_ema_period.js | 28 ++ .../Parts/std_dev_multiplier_down.js | 25 ++ .../Indicators/Parts/std_dev_multiplier_up.js | 28 ++ .../Scratch/Binary/Indicators/bb_statement.js | 90 +++++ .../Binary/Indicators/bba_statement.js | 56 +++ .../Binary/Indicators/ema_statement.js | 47 +++ .../Binary/Indicators/emaa_statement.js | 47 +++ .../blocks/Scratch/Binary/Indicators/index.js | 10 + .../Binary/Indicators/macda_statement.js | 60 ++++ .../Binary/Indicators/rsi_statement.js | 47 +++ .../Binary/Indicators/rsia_statement.js | 47 +++ .../Binary/Indicators/sma_statement.js | 47 +++ .../Binary/Indicators/smaa_statement.js | 46 +++ .../Binary/Tick Analysis/check_direction.js | 52 +++ .../Scratch/Binary/Tick Analysis/get_ohlc.js | 62 ++++ .../Scratch/Binary/Tick Analysis/index.js | 10 + .../Binary/Tick Analysis/lastDigitList.js | 39 ++ .../Binary/Tick Analysis/last_digit.js | 39 ++ .../Scratch/Binary/Tick Analysis/ohlc.js | 54 +++ .../Binary/Tick Analysis/ohlc_values.js | 59 +++ .../Scratch/Binary/Tick Analysis/readOhlc.js | 68 ++++ .../Scratch/Binary/Tick Analysis/tick.js | 39 ++ .../Binary/Tick Analysis/tick_analysis.js | 33 ++ .../Scratch/Binary/Tick Analysis/ticks.js | 39 ++ .../Scratch/Binary/Tools/Candle/index.js | 3 + .../Binary/Tools/Candle/is_candle_black.js | 31 ++ .../Tools/Candle/ohlc_values_in_list.js | 36 ++ .../Binary/Tools/Candle/read_ohlc_obj.js | 35 ++ .../Scratch/Binary/Tools/Misc./balance.js | 43 +++ .../Binary/Tools/Misc./block_holder.js | 25 ++ .../Scratch/Binary/Tools/Misc./index.js | 6 + .../Scratch/Binary/Tools/Misc./loader.js | 68 ++++ .../Scratch/Binary/Tools/Misc./notify.js | 42 +++ .../Binary/Tools/Misc./total_profit.js | 17 + .../Scratch/Binary/Tools/Misc./total_runs.js | 17 + .../blocks/Scratch/Binary/Tools/Time/epoch.js | 17 + .../blocks/Scratch/Binary/Tools/Time/index.js | 2 + .../Scratch/Binary/Tools/Time/timeout.js | 58 +++ .../Scratch/Binary/Trade Definition/index.js | 8 + .../Trade Definition/trade_definition.js | 176 +++++++++ .../trade_definition_candleinterval.js | 35 ++ .../trade_definition_contracttype.js | 70 ++++ .../trade_definition_market.js | 84 +++++ .../trade_definition_restartbuysell.js | 37 ++ .../trade_definition_restartonerror.js | 37 ++ .../trade_definition_tradeoptions.js | 277 +++++++++++++++ .../trade_definition_tradetype.js | 221 ++++++++++++ .../blocks/Scratch/Logic/controls_if.js | 232 ++++++++++++ .../blockly/blocks/Scratch/Logic/index.js | 7 + .../blocks/Scratch/Logic/logic_boolean.js | 27 ++ .../blocks/Scratch/Logic/logic_compare.js | 56 +++ .../blocks/Scratch/Logic/logic_negate.js | 28 ++ .../blocks/Scratch/Logic/logic_null.js | 13 + .../blocks/Scratch/Logic/logic_operation.js | 49 +++ .../blocks/Scratch/Logic/logic_ternary.js | 44 +++ .../view/blockly/blocks/Scratch/Math/index.js | 14 + .../blocks/Scratch/Math/math_arithmetic.js | 57 +++ .../blocks/Scratch/Math/math_change.js | 39 ++ .../blocks/Scratch/Math/math_constant.js | 55 +++ .../blocks/Scratch/Math/math_constrain.js | 40 +++ .../blocks/Scratch/Math/math_modulo.js | 34 ++ .../blocks/Scratch/Math/math_number.js | 24 ++ .../Scratch/Math/math_number_property.js | 112 ++++++ .../blocks/Scratch/Math/math_on_list.js | 159 +++++++++ .../blocks/Scratch/Math/math_random_float.js | 16 + .../blocks/Scratch/Math/math_random_int.js | 46 +++ .../blockly/blocks/Scratch/Math/math_round.js | 46 +++ .../blocks/Scratch/Math/math_single.js | 101 ++++++ .../blockly/blocks/Scratch/Math/math_trig.js | 35 ++ .../view/blockly/blocks/Scratch/Text/index.js | 13 + .../view/blockly/blocks/Scratch/Text/text.js | 23 ++ .../blocks/Scratch/Text/text_append.js | 52 +++ .../blocks/Scratch/Text/text_changeCase.js | 57 +++ .../blocks/Scratch/Text/text_charAt.js | 94 +++++ .../blocks/Scratch/Text/text_getSubstring.js | 163 +++++++++ .../blocks/Scratch/Text/text_indexOf.js | 43 +++ .../blocks/Scratch/Text/text_isEmpty.js | 27 ++ .../blockly/blocks/Scratch/Text/text_join.js | 123 +++++++ .../blocks/Scratch/Text/text_length.js | 27 ++ .../blockly/blocks/Scratch/Text/text_print.js | 26 ++ .../blocks/Scratch/Text/text_prompt_ext.js | 62 ++++ .../blocks/Scratch/Text/text_statement.js | 54 +++ .../blockly/blocks/Scratch/Text/text_trim.js | 43 +++ 131 files changed, 7388 insertions(+) create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callnoreturn.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callreturn.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defnoreturn.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defreturn.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_ifreturn.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_create_with.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getIndex.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getSublist.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_indexOf.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_isEmpty.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_length.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_repeat.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_setIndex.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_sort.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_split.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_flow_statements.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat_ext.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_whileUntil.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_get.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_set.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/after_purchase.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/check_result.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/read_details.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/trade_again.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/ask_price.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/before_purchase.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/payout.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/purchase.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/check_sell.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/during_purchase.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_at_market.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_price.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/fast_ema_period.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/input_list.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/period.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/signal_ema_period.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/slow_ema_period.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/std_dev_multiplier_down.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/std_dev_multiplier_up.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bb_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bba_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/ema_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/emaa_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/macda_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsi_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsia_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/sma_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/smaa_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/check_direction.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/get_ohlc.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/lastDigitList.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/last_digit.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ohlc.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ohlc_values.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/readOhlc.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/tick.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/tick_analysis.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ticks.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/is_candle_black.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/ohlc_values_in_list.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/read_ohlc_obj.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./balance.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./block_holder.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./loader.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./notify.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./total_profit.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./total_runs.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/epoch.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/timeout.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_candleinterval.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_contracttype.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_market.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartbuysell.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartonerror.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradeoptions.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradetype.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Logic/controls_if.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Logic/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Logic/logic_boolean.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Logic/logic_compare.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Logic/logic_negate.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Logic/logic_null.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Logic/logic_operation.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Logic/logic_ternary.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_arithmetic.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_change.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_constant.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_constrain.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_modulo.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_number.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_number_property.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_on_list.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_random_float.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_random_int.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_round.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_single.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Math/math_trig.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/index.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_append.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_changeCase.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_charAt.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_getSubstring.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_indexOf.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_isEmpty.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_join.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_length.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_print.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_prompt_ext.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_statement.js create mode 100644 src/botPage/view/blockly/blocks/Scratch/Text/text_trim.js diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/index.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/index.js new file mode 100644 index 0000000000..abd1716483 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/index.js @@ -0,0 +1,5 @@ +import './procedures_defnoreturn'; +import './procedures_defreturn'; +import './procedures_callnoreturn'; +import './procedures_callreturn'; +import './procedures_ifreturn'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callnoreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callnoreturn.js new file mode 100644 index 0000000000..04219894ef --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callnoreturn.js @@ -0,0 +1,335 @@ +import { setBlockTextColor } from '../../../../utils'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.procedures_callnoreturn = { + init() { + this.arguments_ = []; + this.argumentVarModels_ = []; + this.previousDisabledState_ = false; + + this.jsonInit({ + message0: '%1 %2', + args0 : [ + { + type: 'field_label', + name: 'NAME', + text: this.id, + }, + { + type: 'input_dummy', + name: 'TOPROW', + }, + ], + colour : Blockly.Colours.BinaryProcedures.colour, + colourSecondary : Blockly.Colours.BinaryProcedures.colourSecondary, + colourTertiary : Blockly.Colours.BinaryProcedures.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, + /** + * Procedure calls cannot exist without the corresponding procedure + * definition. Enforce this link whenever an event is fired. + * @param {!Blockly.Events.Abstract} event Change event. + * @this Blockly.Block + */ + onchange(event) { + setBlockTextColor(this); + + if (!this.workspace || this.workspace.isFlyout) { + // Block is deleted or is in a flyout. + return; + } + + if (!event.recordUndo) { + // Events not generated by user. Skip handling. + return; + } + + if (event.type == Blockly.Events.BLOCK_CREATE && event.ids.indexOf(this.id) !== -1) { + // Look for the case where a procedure call was created (usually through + // paste) and there is no matching definition. In this case, create + // an empty definition block with the correct signature. + const name = this.getProcedureCall(); + const def = this.getProcedureDefinition(name); + + // Set data of `this` block to 'procedure definition'-block `id` so we can keep track of their relation. + if (!def) { + this.unplug(); + this.dispose(); + } + + this.data = def.id; + + if ( + def && + (def.type !== this.defType_ || JSON.stringify(def.arguments_) !== JSON.stringify(this.arguments_)) + ) { + // The signatures don't match. + def = null; + } + if (!def) { + Blockly.Events.setGroup(event.group); + /** + * Create matching definition block. + * + * + * + * + * + * test + * + * + */ + const xml = document.createElement('xml'); + const block = document.createElement('block'); + block.setAttribute('type', this.defType_); + + const xy = this.getRelativeToSurfaceXY(); + const x = xy.x + Blockly.SNAP_RADIUS * (this.RTL ? -1 : 1); + const y = xy.y + Blockly.SNAP_RADIUS * 2; + + block.setAttribute('x', x); + block.setAttribute('y', y); + + const mutation = this.mutationToDom(); + + block.appendChild(mutation); + + const field = document.createElement('field'); + field.setAttribute('name', 'NAME'); + field.appendChild(document.createTextNode(this.getProcedureCall())); + + block.appendChild(field); + xml.appendChild(block); + + Blockly.Xml.domToWorkspace(xml, this.workspace); + Blockly.Events.setGroup(false); + } + } else if (event.type === Blockly.Events.BLOCK_DELETE) { + // Look for the case where a procedure definition has been deleted, + // leaving this block (a procedure call) orphaned. In this case, delete + // the orphan. + const name = this.getProcedureCall(); + const def = Blockly.Procedures.getDefinition(name, this.workspace); + + if (!def) { + Blockly.Events.setGroup(event.group); + this.dispose(true, false); + Blockly.Events.setGroup(false); + } + } else if (event.type === Blockly.Events.BLOCK_CHANGE && event.element === 'disabled') { + const name = this.getProcedureCall(); + const def = Blockly.Procedures.getDefinition(name, this.workspace); + + if (def && def.id === event.blockId) { + // in most cases the old group should be '' + const oldGroup = Blockly.Events.getGroup(); + + if (oldGroup) { + // This should only be possible programatically and may indicate a problem + // with event grouping. If you see this message please investigate. If the + // use ends up being valid we may need to reorder events in the undo stack. + console.log('Saw an existing group while responding to a definition change'); + } + + Blockly.Events.setGroup(event.group); + + if (event.newValue) { + this.previousDisabledState_ = this.disabled; + this.setDisabled(true); + } else { + this.setDisabled(this.previousDisabledState_); + } + + Blockly.Events.setGroup(oldGroup); + } + } + }, + /** + * Returns the related procedure definition block. + * @return {Blockly.Block} Procedure definition block. + * @this Blockly.Block + */ + getProcedureDefinition(name) { + // Assume that a procedure definition is a top block. + return this.workspace.getTopBlocks(false).find(block => { + if (block.getProcedureDef) { + const tuple = block.getProcedureDef(); + return tuple && Blockly.Names.equals(tuple[0], name); + } + return false; + }); + }, + /** + * Returns the name of the procedure this block calls. + * @return {string} Procedure name. + * @this Blockly.Block + */ + getProcedureCall() { + // The NAME field is guaranteed to exist, null will never be returned. + return /** @type {string} */ (this.getFieldValue('NAME')); + }, + /** + * Notification that a procedure is renaming. + * If the name matches this block's procedure, rename it. + * @param {string} oldName Previous name of procedure. + * @param {string} newName Renamed procedure. + * @this Blockly.Block + */ + renameProcedure(oldName, newName) { + if (Blockly.Names.equals(oldName, this.getProcedureCall())) { + this.setFieldValue(newName, 'NAME'); + } + }, + /** + * Notification that the procedure's parameters have changed. + * @param {!Array.} paramNames New param names, e.g. ['x', 'y', 'z']. + * @private + * @this Blockly.Block + */ + setProcedureParameters_(paramNames) { + // Rebuild the block's arguments. + this.arguments_ = [].concat(paramNames); + + // And rebuild the argument model list. + this.argumentVarModels_ = this.arguments_.map(argumentName => + Blockly.Variables.getOrCreateVariablePackage(this.workspace, null, argumentName, '') + ); + + this.updateShape_(); + }, + /** + * Modify this block to have the correct number of arguments. + * @private + * @this Blockly.Block + */ + updateShape_() { + this.arguments_.forEach((argumentName, i) => { + let field = this.getField(`ARGNAME${i}`); + if (field) { + // Ensure argument name is up to date. + // The argument name field is deterministic based on the mutation, + // no need to fire a change event. + Blockly.Events.disable(); + try { + field.setValue(argumentName); + } finally { + Blockly.Events.enable(); + } + } else { + // Add new input. + field = new Blockly.FieldLabel(argumentName); + const input = this.appendValueInput(`ARG${i}`).appendField(field, `ARGNAME${i}`); + input.init(); + } + }); + + // Remove deleted inputs. + while (this.getInput(`ARG${i}`)) { + this.removeInput(`ARG${i}`); + i++; + } + + // Add 'with:' if there are parameters, remove otherwise. + const topRow = this.getInput('TOPROW'); + + if (topRow) { + if (this.arguments_.length) { + if (!this.getField('WITH')) { + topRow.appendField(translate('with:'), 'WITH'); + topRow.init(); + } + } else if (this.getField('WITH')) { + topRow.removeField('WITH'); + } + } + }, + /** + * Create XML to represent the (non-editable) name and arguments. + * @return {!Element} XML storage element. + * @this Blockly.Block + */ + mutationToDom() { + const container = document.createElement('mutation'); + container.setAttribute('name', this.getProcedureCall()); + + this.arguments_.forEach(argumentName => { + const parameter = document.createElement('arg'); + parameter.setAttribute('name', argumentName); + container.appendChild(parameter); + }); + + return container; + }, + /** + * Parse XML to restore the (non-editable) name and parameters. + * @param {!Element} xmlElement XML storage element. + * @this Blockly.Block + */ + domToMutation(xmlElement) { + const name = xmlElement.getAttribute('name'); + this.renameProcedure(this.getProcedureCall(), name); + + const args = []; + const paramIds = []; + + xmlElement.childNodes.forEach(childNode => { + if (childNode.nodeName.toLowerCase() === 'arg') { + args.push(childNode.getAttribute('name')); + paramIds.push(childNode.getAttribute('paramId')); + } + }); + + this.setProcedureParameters_(args, paramIds); + }, + /** + * Return all variables referenced by this block. + * @return {!Array.} List of variable models. + * @this Blockly.Block + */ + getVarModels() { + return this.argumentVarModels_; + }, + /** + * Add menu option to find the definition block for this call. + * @param {!Array} options List of menu options to add to. + * @this Blockly.Block + */ + customContextMenu(options) { + if (!this.workspace.isMovable()) { + // If we center on the block and the workspace isn't movable we could + // loose blocks at the edges of the workspace. + return; + } + + const name = this.getProcedureCall(); + const workspace = this.workspace; + + const option = { enabled: true }; + option.text = translate('Highlight function definition'); + option.callback = () => { + const def = this.getProcedureDefinition(name); + if (def) { + workspace.centerOnBlock(def.id); + def.select(); + } + }; + + options.push(option); + }, + defType_: 'procedures_defnoreturn', +}; + +Blockly.JavaScript.procedures_callnoreturn = block => { + const functionName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('NAME'), + Blockly.Procedures.NAME_TYPE + ); + const args = block.arguments_.map( + (arg, i) => Blockly.JavaScript.valueToCode(block, `ARG${i}`, Blockly.JavaScript.ORDER_COMMA) || 'null' + ); + + const code = `${functionName}(${args.join(', ')});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callreturn.js new file mode 100644 index 0000000000..5178289d22 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callreturn.js @@ -0,0 +1,38 @@ +Blockly.Blocks.procedures_callreturn = { + init() { + this.arguments_ = []; + this.previousDisabledState_ = false; + + this.jsonInit({ + message0: '%1 %2', + args0 : [ + { + type: 'field_label', + name: 'NAME', + text: this.id, + }, + { + type: 'input_dummy', + name: 'TOPROW', + }, + ], + output : null, + colour : Blockly.Colours.BinaryProcedures.colour, + colourSecondary: Blockly.Colours.BinaryProcedures.colourSecondary, + colourTertiary : Blockly.Colours.BinaryProcedures.colourTertiary, + }); + }, + onchange : Blockly.Blocks.procedures_callnoreturn.onchange, + getProcedureDefinition : Blockly.Blocks.procedures_callnoreturn.getProcedureDefinition, + getProcedureCall : Blockly.Blocks.procedures_callnoreturn.getProcedureCall, + renameProcedure : Blockly.Blocks.procedures_callnoreturn.renameProcedure, + setProcedureParameters_: Blockly.Blocks.procedures_callnoreturn.setProcedureParameters_, + updateShape_ : Blockly.Blocks.procedures_callnoreturn.updateShape_, + mutationToDom : Blockly.Blocks.procedures_callnoreturn.mutationToDom, + domToMutation : Blockly.Blocks.procedures_callnoreturn.domToMutation, + getVarModels : Blockly.Blocks.procedures_callnoreturn.getVarModels, + customContextMenu : Blockly.Blocks.procedures_callnoreturn.customContextMenu, + defType_ : 'procedures_defreturn', +}; + +Blockly.JavaScript.procedures_callreturn = Blockly.JavaScript.procedures_callnoreturn; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defnoreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defnoreturn.js new file mode 100644 index 0000000000..6003706e3e --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defnoreturn.js @@ -0,0 +1,335 @@ +import { plusIconLight } from '../../../images'; +import { setBlockTextColor } from '../../../../utils'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.procedures_defnoreturn = { + init() { + this.arguments_ = []; + this.argumentVarModels_ = []; + + this.jsonInit({ + message0: translate('function %1 %2'), + args0 : [ + { + type: 'field_input', + name: 'NAME', + text: '', + }, + { + type: 'field_label', + name: 'PARAMS', + text: '', + }, + ], + colour : Blockly.Colours.BinaryProcedures.colour, + colourSecondary: Blockly.Colours.BinaryProcedures.colourSecondary, + colourTertiary : Blockly.Colours.BinaryProcedures.colourTertiary, + }); + + // Enforce unique procedure names + const nameField = this.getField('NAME'); + nameField.setValidator(Blockly.Procedures.rename); + + // Render a ➕-icon for adding parameters + const fieldImage = new Blockly.FieldImage(plusIconLight, 24, 24, '+', () => this.onAddClick_()); + this.appendDummyInput('ADD_ICON').appendField(fieldImage); + + this.setStatements_(true); + this.registerWorkspaceListener(); + }, + /** + * Sets the block colour and updates this procedure's caller blocks + * to reflect the same name on a change. + * @param {!Blockly.Events.Abstract} event Change event. + * @this Blockly.Block + */ + onchange(event) { + setBlockTextColor(this); + + const allowedEvents = [Blockly.Events.BLOCK_DELETE, Blockly.Events.BLOCK_CREATE, Blockly.Events.BLOCK_CHANGE]; + if (!this.workspace || this.workspace.isFlyout || !allowedEvents.includes(event.type)) { + return; + } + + if (event.type === Blockly.Events.BLOCK_CHANGE) { + // Sync names between definition- and execution-block + if (event.blockId === this.id && event.name === 'NAME') { + this.getProcedureCallers().forEach(block => { + block.setFieldValue(event.newValue, 'NAME'); + }); + } + } + }, + /** + * Prompt the user for parameter name + * @this Blockly.Block + */ + onAddClick_() { + // Wrap in setTimeout so block doesn't stick to mouse (Blockly.Events.END_DRAG event isn't blocked). + setTimeout(() => { + const promptMessage = translate('Specify a parameter name:'); + Blockly.prompt(promptMessage, '', paramName => { + if (paramName) { + const variable = Blockly.Variables.getOrCreateVariablePackage(this.workspace, null, paramName, ''); + if (variable) { + this.arguments_.push(paramName); + this.argumentVarModels_.push(variable); + + const paramField = this.getField('PARAMS'); + paramField.setText(`${translate('with: ')} ${this.arguments_.join(', ')}`); + } + } + }); + }, 200); + }, + /** + * Add or remove the statement block from this function definition. + * @param {boolean} hasStatements True if a statement block is needed. + * @this Blockly.Block + */ + setStatements_(hasStatements) { + if (this.hasStatements_ === hasStatements) { + return; + } + + if (hasStatements) { + this.appendStatementInput('STACK').appendField(''); + if (this.getInput('RETURN')) { + this.moveInputBefore('STACK', 'RETURN'); + } + } else { + this.removeInput('STACK', true); + } + + this.hasStatements_ = hasStatements; + }, + /** + * Update the display of parameters for this procedure definition block. + * @private + * @this Blockly.Block + */ + updateParams_() { + let paramString = ''; + + if (this.arguments_.length) { + paramString = `${translate('with:')} ${this.arguments_.join(', ')}`; + } + + // The params field is deterministic based on the mutation, + // no need to fire a change event. + Blockly.Events.disable(); + try { + this.setFieldValue(paramString, 'PARAMS'); + } finally { + Blockly.Events.enable(); + } + }, + /** + * Create XML to represent the argument inputs. + * @param {boolean=} opt_paramIds If true include the IDs of the parameter + * quarks. Used by Blockly.Procedures.mutateCallers for reconnection. + * @return {!Element} XML storage element. + * @this Blockly.Block + */ + mutationToDom(opt_paramIds) { + const container = document.createElement('mutation'); + + if (opt_paramIds) { + container.setAttribute('name', this.getFieldValue('NAME')); + } + + this.argumentVarModels_.forEach((arg, i) => { + const parameter = document.createElement('arg'); + + parameter.setAttribute('name', arg.name); + parameter.setAttribute('varid', arg.getId()); + + if (opt_paramIds && this.paramIds_) { + parameter.setAttribute('paramId', this.paramIds_[i]); + } + container.appendChild(parameter); + }); + + // Save whether the statement input is visible. + if (!this.hasStatements_) { + container.setAttribute('statements', 'false'); + } + + return container; + }, + /** + * Parse XML to restore the argument inputs. + * @param {!Element} xmlElement XML storage element. + * @this Blockly.Block + */ + domToMutation(xmlElement) { + this.arguments_ = []; + this.argumentVarModels_ = []; + + for (let i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { + if (childNode.nodeName.toLowerCase() === 'arg') { + const varName = childNode.getAttribute('name'); + this.arguments_.push(varName); + + const varId = childNode.getAttribute('varid') || childNode.getAttribute('varId'); + const variable = Blockly.Variables.getOrCreateVariablePackage(this.workspace, varId, varName, ''); + + if (variable !== null) { + this.argumentVarModels_.push(variable); + } else { + console.log(`Failed to create a variable with name ${varName}, ignoring.`); + } + } + } + + this.updateParams_(); + + // Show or hide the statement input. + this.setStatements_(xmlElement.getAttribute('statements') !== 'false'); + }, + /** + * Return the signature of this procedure definition. + * @return {!Array} Tuple containing three elements: + * - the name of the defined procedure, + * - a list of all its arguments, + * - that it DOES NOT have a return value. + * @this Blockly.Block + */ + getProcedureDef() { + return [this.getFieldValue('NAME'), this.arguments_, false]; + }, + /** + * Return all procedure callers related to this block. + * @return {!Array.} List of procedure caller blocks + * @this Blockly.Block + */ + getProcedureCallers() { + return this.workspace + .getAllBlocks(false) + .filter(block => block.type === this.callType_ && block.data === this.id); + }, + /** + * Return all variables referenced by this block. + * @return {!Array.} List of variable names. + * @this Blockly.Block + */ + getVars() { + return this.arguments_; + }, + /** + * Return all variables referenced by this block. + * @return {!Array.} List of variable models. + * @this Blockly.Block + */ + getVarModels() { + return this.argumentVarModels_; + }, + /** + * Add custom menu options to this block's context menu. + * @param {!Array} options List of menu options to add to. + * @this Blockly.Block + */ + customContextMenu(options) { + if (this.isInFlyout) { + return; + } + // Add option to create caller. + var option = { enabled: true }; + var name = this.getFieldValue('NAME'); + option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name); + const xmlMutation = document.createElement('mutation'); + xmlMutation.setAttribute('name', name); + for (var i = 0; i < this.arguments_.length; i++) { + const xmlArg = document.createElement('arg'); + xmlArg.setAttribute('name', this.arguments_[i]); + xmlMutation.appendChild(xmlArg); + } + var xmlBlock = document.createElement('block'); + xmlBlock.setAttribute('type', this.callType_); + xmlBlock.appendChild(xmlMutation); + option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + options.push(option); + + // Add options to create getters for each parameter. + if (!this.isCollapsed()) { + for (var i = 0; i < this.argumentVarModels_.length; i++) { + var option = { enabled: true }; + const argVar = this.argumentVarModels_[i]; + var name = argVar.name; + option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name); + + const xmlField = Blockly.Variables.generateVariableFieldDom(argVar); + var xmlBlock = document.createElement('block'); + xmlBlock.setAttribute('type', 'variables_get'); + xmlBlock.appendChild(xmlField); + option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + options.push(option); + } + } + }, + callType_: 'procedures_callnoreturn', + /** + * TODO: Registers an event to the workspace this procedure is on once. + * This listener will clean up variables that aren't used by any procedure + * and that aren't created by the user. This listener will persist through + * block deletion. + * @this Blockly.Block + */ + registerWorkspaceListener() { + if (!this.workspace || this.isInFlyout) { + return; + } + + const workspaceListener = event => {}; + + const hasProcedureListener = this.workspace.listeners_.find(func => func.name === 'workspaceListener'); + if (!hasProcedureListener) { + this.workspace.addChangeListener(workspaceListener); + } + }, +}; + +Blockly.JavaScript.procedures_defnoreturn = block => { + const functionName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('NAME'), + Blockly.Procedures.NAME_TYPE + ); + + let branch = Blockly.JavaScript.statementToCode(block, 'STACK'); + + if (Blockly.JavaScript.STATEMENT_PREFIX) { + const id = block.id.replace(/\$/g, '$$$$'); // Issue 251. + + branch = + Blockly.JavaScript.prefixLines( + Blockly.JavaScript.STATEMENT_PREFIX.replace(/%1/g, `'${id}'`), + Blockly.JavaScript.INDENT + ) + branch; + } + + if (Blockly.JavaScript.INFINITE_LOOP_TRAP) { + branch = Blockly.JavaScript.INFINITE_LOOP_TRAP.replace(/%1/g, `'${block.id}'`) + branch; + } + + let returnValue = Blockly.JavaScript.valueToCode(block, 'RETURN', Blockly.JavaScript.ORDER_NONE) || ''; + if (returnValue) { + returnValue = `${Blockly.JavaScript.INDENT}return ${returnValue};\n`; + } + + const args = block.arguments_.map(argumentName => + Blockly.JavaScript.variableDB_.getName(argumentName, Blockly.Variables.NAME_TYPE) + ); + + const code = Blockly.JavaScript.scrub_( + block, + ` + function ${functionName}(${args.join(', ')}) { + ${branch} + ${returnValue} + }\n` + ); + + // Add % so as not to collide with helper functions in definitions list. + Blockly.JavaScript.definitions_[`%${functionName}`] = code; + return null; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defreturn.js new file mode 100644 index 0000000000..bd153d097f --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defreturn.js @@ -0,0 +1,80 @@ +import { translate } from '../../../../../../../common/utils/tools'; +import { plusIconDark } from '../../../images'; + +Blockly.Blocks.procedures_defreturn = { + init() { + this.arguments_ = []; + this.argumentVarModels_ = []; + + this.jsonInit({ + message0: translate('function %1 %2 %3'), + message1: 'return %1', + args0 : [ + { + type: 'field_input', + name: 'NAME', + text: '', + }, + { + type: 'field_label', + name: 'PARAMS', + text: '', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_value', + name : 'RETURN', + check: null, + align: Blockly.ALIGN_RIGHT, + }, + ], + colour : Blockly.Colours.BinaryProcedures.colour, + colourSecondary: Blockly.Colours.BinaryProcedures.colourSecondary, + colourTertiary : Blockly.Colours.BinaryProcedures.colourTertiary, + }); + + // Enforce unique procedure names + const nameField = this.getField('NAME'); + nameField.setValidator(Blockly.Procedures.rename); + + // Render a ➕-icon for adding parameters + const fieldImage = new Blockly.FieldImage(plusIconDark, 24, 24, '+', () => this.onAddClick_()); + this.appendDummyInput('ADD_ICON').appendField(fieldImage); + this.moveInputBefore('ADD_ICON', 'RETURN'); + + this.setStatements_(true); + this.registerWorkspaceListener(); + }, + onAddClick_ : Blockly.Blocks.procedures_defnoreturn.onAddClick_, + onchange : Blockly.Blocks.procedures_defnoreturn.onchange, + setStatements_: Blockly.Blocks.procedures_defnoreturn.setStatements_, + updateParams_ : Blockly.Blocks.procedures_defnoreturn.updateParams_, + mutationToDom : Blockly.Blocks.procedures_defnoreturn.mutationToDom, + domToMutation : Blockly.Blocks.procedures_defnoreturn.domToMutation, + /** + * Return the signature of this procedure definition. + * @return {!Array} Tuple containing three elements: + * - the name of the defined procedure, + * - a list of all its arguments, + * - that it DOES have a return value. + * @this Blockly.Block + */ + getProcedureDef() { + return [this.getFieldValue('NAME'), this.arguments_, true]; + }, + getProcedureCallers : Blockly.Blocks.procedures_defnoreturn.getProcedureCallers, + getVars : Blockly.Blocks.procedures_defnoreturn.getVars, + getVarModels : Blockly.Blocks.procedures_defnoreturn.getVarModels, + renameVarById : Blockly.Blocks.procedures_defnoreturn.renameVarById, + updateVarName : Blockly.Blocks.procedures_defnoreturn.updateVarName, + displayRenamedVar_ : Blockly.Blocks.procedures_defnoreturn.displayRenamedVar_, + customContextMenu : Blockly.Blocks.procedures_defnoreturn.customContextMenu, + callType_ : 'procedures_callreturn', + registerWorkspaceListener: Blockly.Blocks.procedures_defnoreturn.registerWorkspaceListener, +}; + +Blockly.JavaScript.procedures_defreturn = Blockly.JavaScript.procedures_defnoreturn; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_ifreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_ifreturn.js new file mode 100644 index 0000000000..80cfd1aa64 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_ifreturn.js @@ -0,0 +1,132 @@ +import { translate } from '../../../../../../../common/i18n'; +import { setBlockTextColor } from '../../../../utils'; + +/** + * Block for conditionally returning a value from a procedure. + * @this Blockly.Block + */ +Blockly.Blocks.procedures_ifreturn = { + init() { + this.hasReturnValue_ = true; + + this.jsonInit({ + message0: translate('if %1 return %2'), + args0 : [ + { + type: 'input_value', + name: 'CONDITION', + }, + { + type: 'input_value', + name: 'VALUE', + }, + ], + colour : Blockly.Colours.BinaryProcedures.colour, + colourSecondary : Blockly.Colours.BinaryProcedures.colourSecondary, + colourTertiary : Blockly.Colours.BinaryProcedures.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, + /** + * Create XML to represent whether this block has a return value. + * @return {!Element} XML storage element. + * @this Blockly.Block + */ + mutationToDom() { + const container = document.createElement('mutation'); + container.setAttribute('value', Number(this.hasReturnValue_)); + return container; + }, + /** + * Parse XML to restore whether this block has a return value. + * @param {!Element} xmlElement XML storage element. + * @this Blockly.Block + */ + domToMutation(xmlElement) { + const value = xmlElement.getAttribute('value'); + this.hasReturnValue_ = value === 1; + + if (!this.hasReturnValue_) { + this.removeInput('VALUE'); + this.appendDummyInput('VALUE').appendField(translate('return')); + this.initSvg(); + this.render(); + } + }, + /** + * Called whenever anything on the workspace changes. + * Add warning if this flow block is not nested inside a loop. + * @param {!Blockly.Events.Abstract} e Change event. + * @this Blockly.Block + */ + onchange(/* e */) { + setBlockTextColor(this); + + if (!this.workspace.isDragging || this.workspace.isDragging()) { + return; // Don't change state at the start of a drag. + } + + let legal = false; + + // Is the block nested in a procedure? + let block = this; + do { + if (this.FUNCTION_TYPES.indexOf(block.type) !== -1) { + legal = true; + break; + } + block = block.getSurroundParent(); + } while (block); + + if (legal) { + const rerender = () => { + this.initSvg(); + this.render(); + }; + + // If needed, toggle whether this block has a return value. + if (block.type == 'procedures_defnoreturn' && this.hasReturnValue_) { + this.removeInput('VALUE'); + this.appendDummyInput('VALUE').appendField(translate('return')); + rerender(); + this.hasReturnValue_ = false; + } else if (block.type == 'procedures_defreturn' && !this.hasReturnValue_) { + this.removeInput('VALUE'); + this.appendValueInput('VALUE').appendField(translate('return')); + rerender(); + this.hasReturnValue_ = true; + } + + if (!this.isInFlyout) { + this.setDisabled(false); + } + } else if (!this.isInFlyout && !this.getInheritedDisabled()) { + this.setDisabled(true); + } + }, + /** + * List of block types that are functions and thus do not need warnings. + * To add a new function type add this to your code: + * Blockly.Blocks['procedures_ifreturn'].FUNCTION_TYPES.push('custom_func'); + */ + FUNCTION_TYPES: ['procedures_defnoreturn', 'procedures_defreturn'], +}; + +Blockly.JavaScript.procedures_ifreturn = block => { + const condition = Blockly.JavaScript.valueToCode(block, 'CONDITION', Blockly.JavaScript.ORDER_NONE) || 'false'; + + let branch; + if (block.hasReturnValue_) { + const value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_NONE) || 'null'; + branch = `return ${value};\n`; + } else { + branch = 'return;\n'; + } + + const code = ` + if ${condition} { + ${branch} + }\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/index.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/index.js new file mode 100644 index 0000000000..a0e7ed8038 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/index.js @@ -0,0 +1,11 @@ +import './lists_create_with'; +import './lists_repeat'; +import './lists_length'; +import './lists_isEmpty'; +import './lists_indexOf'; +import './lists_getIndex'; +import './lists_setIndex'; +import './lists_getSublist'; +import './lists_split'; +import './lists_sort'; +import './lists_statement'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_create_with.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_create_with.js new file mode 100644 index 0000000000..9e61a55c6f --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_create_with.js @@ -0,0 +1,85 @@ +import { translate } from '../../../../../../../common/i18n'; +import { plusIconDark } from '../../../images'; + +Blockly.Blocks.lists_create_with = { + init() { + this.jsonInit({ + message0: translate('set %1 to create list with'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: translate('list'), + }, + ], + args1: [ + { + type: 'input_statement', + name: 'STACK', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + // Render a ➕-icon for adding additional `lists_statement` blocks + const fieldImage = new Blockly.FieldImage(plusIconDark, 25, 25, '', () => this.onIconClick()); + this.appendDummyInput('ADD_ICON').appendField(fieldImage); + this.moveInputBefore('ADD_ICON', 'STACK'); + }, + onIconClick() { + if (!this.workspace || this.isInFlyout) { + return; + } + + const statementBlock = this.workspace.newBlock('lists_statement'); + statementBlock.setMovable(false); + statementBlock.initSvg(); + statementBlock.render(); + + const connection = this.getLastConnectionInStatement('STACK'); + connection.connect(statementBlock.previousConnection); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + // Only allow `lists_statement` blocks to be part of the `STACK` + let currentBlock = this.getInputTargetBlock('STACK'); + while (currentBlock !== null) { + if (currentBlock.type !== 'lists_statement') { + currentBlock.unplug(false); + } + currentBlock = currentBlock.getNextBlock(); + } + } + }, +}; + +Blockly.JavaScript.lists_create_with = block => { + const variable = block.getFieldValue('VARIABLE'); + const varName = Blockly.JavaScript.variableDB_.getName(variable, Blockly.Variables.NAME_TYPE); + const elements = []; + + let currentBlock = block.getInputTargetBlock('STACK'); + while (currentBlock !== null) { + const value = Blockly.JavaScript[currentBlock.type](currentBlock); + + if (Array.isArray(value) && value.length === 2) { + elements.push(value[0]); + } else { + elements.push(value); + } + + currentBlock = currentBlock.getNextBlock(); + } + + const code = `${varName} = [${elements.join(', ')}];\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getIndex.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getIndex.js new file mode 100644 index 0000000000..0f2e1403e6 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getIndex.js @@ -0,0 +1,166 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_getIndex = { + init() { + this.MODE_OPTIONS = [ + [translate('get'), 'GET'], + [translate('get and remove'), 'GET_REMOVE'], + [translate('remove'), 'REMOVE'], + ]; + this.WHERE_OPTIONS = [ + ['#', 'FROM_START'], + [translate('# from end'), 'FROM_END'], + [translate('first'), 'FIRST'], + [translate('last'), 'LAST'], + [translate('random'), 'RANDOM'], + ]; + const modeMenu = new Blockly.FieldDropdown(this.MODE_OPTIONS, value => { + const isStatement = value === 'REMOVE'; + this.updateStatement_(isStatement); + }); + + this.appendValueInput('VALUE') + .setCheck('Array') + .appendField(translate('in list')); + this.appendDummyInput().appendField(modeMenu, 'MODE'); + this.appendDummyInput('AT'); + this.setColourFromRawValues_( + Blockly.Colours.Binary.colour, + Blockly.Colours.Binary.colourSecondary, + Blockly.Colours.Binary.colourTertiary + ); + this.setOutput(true, null); + + this.updateAt_(true); + }, + mutationToDom() { + const container = document.createElement('mutation'); + const isStatement = !this.outputConnection; + const isAt = this.getInput('AT').type === Blockly.INPUT_VALUE; + + container.setAttribute('statement', isStatement); + container.setAttribute('at', isAt); + + return container; + }, + domToMutation(xmlElement) { + const isStatement = xmlElement.getAttribute('statement') === 'true'; + this.updateStatement_(isStatement); + + const isAt = xmlElement.getAttribute('at') !== 'false'; + this.updateAt_(isAt); + }, + updateStatement_(newStatement) { + const oldStatement = !this.outputConnection; + + if (newStatement !== oldStatement) { + this.unplug(true, true); + + this.setOutput(!newStatement); + this.setPreviousStatement(newStatement); + this.setNextStatement(newStatement); + + this.initSvg(); + this.render(false); + } + }, + updateAt_(isAt) { + this.removeInput('AT', true); + + if (isAt) { + this.appendValueInput('AT').setCheck('Number'); + } else { + this.appendDummyInput('AT'); + } + + const menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, value => { + const newAt = ['FROM_START', 'FROM_END'].includes(value); + if (newAt !== isAt) { + this.updateAt_(newAt); + this.setFieldValue(value, 'WHERE'); + return null; + } + return undefined; + }); + + this.getInput('AT').appendField(menu, 'WHERE'); + + this.initSvg(); + this.render(false); + }, +}; + +Blockly.JavaScript.lists_getIndex = block => { + const mode = block.getFieldValue('MODE') || 'GET'; + const where = block.getFieldValue('WHERE') || 'FIRST'; + const listOrder = where === 'RANDOM' ? Blockly.JavaScript.ORDER_COMMA : Blockly.JavaScript.ORDER_MEMBER; + const list = Blockly.JavaScript.valueToCode(block, 'VALUE', listOrder) || '[]'; + + let code; + let order; + + if (where === 'FIRST') { + if (mode === 'GET') { + code = `${list}[0]`; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (mode === 'GET_REMOVE') { + code = `${list}.shift()`; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (mode === 'REMOVE') { + return `${list}.shift();\n`; + } + } else if (where === 'LAST') { + if (mode === 'GET') { + code = `${list}.slice(-1)[0]`; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (mode === 'GET_REMOVE') { + code = `${list}.pop()`; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (mode === 'REMOVE') { + return `${list}.pop();\n`; + } + } else if (where === 'FROM_START') { + const at = Blockly.JavaScript.getAdjusted(block, 'AT'); + if (mode === 'GET') { + code = `${list}[${at}]`; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (mode === 'GET_REMOVE') { + code = `${list}.splice(${at}, 1)[0]`; + order = Blockly.JavaScript.ORDER_FUNCTION_CALL; + } else if (mode === 'REMOVE') { + return `${list}.splice(${at}, 1);\n`; + } + } else if (where === 'FROM_END') { + const at = Blockly.JavaScript.getAdjusted(block, 'AT1', 1, true); + if (mode === 'GET') { + code = `${list}.slice(${at})[0]`; + order = Blockly.JavaScript.ORDER_FUNCTION_CALL; + } else if (mode === 'GET_REMOVE') { + code = `${list}.splice(${at}, 1)[0]`; + order = Blockly.JavaScript.ORDER_FUNCTION_CALL; + } else if (mode === 'REMOVE') { + return `${list}.splice(${at}, 1);\n`; + } + } else if (where === 'RANDOM') { + const functionName = Blockly.JavaScript.provideFunction_('listsGetRandomItem', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { + var x = Math.floor(Math.random() * list.length); + if (remove) { + return list.splice(x, 1)[0]; + } else { + return list[x]; + } + }`, + ]); + + code = `${functionName}(${list}, ${mode !== 'GET'})`; + + if (mode === 'GET' || mode === 'GET_REMOVE') { + order = Blockly.JavaScript.ORDER_FUNCTION_CALL; + } else if (mode === 'REMOVE') { + return `${code};\n`; + } + } + + return [code, order]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getSublist.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getSublist.js new file mode 100644 index 0000000000..198aff1c16 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getSublist.js @@ -0,0 +1,125 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_getSublist = { + init() { + this.WHERE_OPTIONS_1 = [ + [translate('get sub-list from #'), 'FROM_START'], + [translate('get sub-list from # from end'), 'FROM_END'], + [translate('get sub-list from first'), 'FIRST'], + ]; + this.WHERE_OPTIONS_2 = [ + [translate('#'), 'FROM_START'], + [translate('# from end'), 'FROM_END'], + [translate('last'), 'LAST'], + ]; + + this.appendValueInput('LIST').appendField(translate('in list')); + this.appendDummyInput('AT1'); + this.appendDummyInput('AT2'); + + this.setColourFromRawValues_( + Blockly.Colours.Binary.colour, + Blockly.Colours.Binary.colourSecondary, + Blockly.Colours.Binary.colourTertiary + ); + this.setOutput(true, null); + this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); + + this.updateAt_(1, true); + this.updateAt_(2, true); + }, + mutationToDom() { + const container = document.createElement('mutation'); + const isAt1 = this.getInput('AT1').type === Blockly.INPUT_VALUE; + const isAt2 = this.getInput('AT2').type === Blockly.INPUT_VALUE; + + container.setAttribute('at1', isAt1); + container.setAttribute('at2', isAt2); + + return container; + }, + domToMutation(xmlElement) { + const isAt1 = xmlElement.getAttribute('at1') === 'true'; + const isAt2 = xmlElement.getAttribute('at2') === 'true'; + this.updateAt_(1, isAt1); + this.updateAt_(2, isAt2); + }, + updateAt_(n, isAt) { + this.removeInput(`AT${n}`); + + if (isAt) { + this.appendValueInput(`AT${n}`).setCheck('Number'); + } else { + this.appendDummyInput(`AT${n}`); + } + + const menu = new Blockly.FieldDropdown(this[`WHERE_OPTIONS_${n}`], value => { + const newAt = ['FROM_START', 'FROM_END'].includes(value); + if (newAt !== isAt) { + this.updateAt_(n, newAt); + this.setFieldValue(value, `WHERE${n}`); + return null; + } + return undefined; + }); + + this.getInput(`AT${n}`).appendField(menu, `WHERE${n}`); + + if (n === 1) { + this.moveInputBefore('AT1', 'AT2'); + } + + this.initSvg(); + this.render(false); + }, +}; + +Blockly.JavaScript.lists_getSublist = block => { + const list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_MEMBER) || '[]'; + const where1 = block.getFieldValue('WHERE1'); + const where2 = block.getFieldValue('WHERE2'); + + let at1; + let at2; + let code; + + if (list.match(/^\w+$/)) { + if (where1 === 'FROM_START') { + at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + } else if (where1 === 'FROM_END') { + at1 = Blockly.JavaScript.getAdjusted(block, 'AT1', 1, false, Blockly.JavaScript.ORDER_SUBTRACTION); + at1 = `${list}.length - ${at1}`; + } + if (where2 === 'FROM_START') { + at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 1); + } else if (where2 === 'FROM_END') { + at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 0, false, Blockly.JavaScript.ORDER_SUBTRACTION); + at2 = `${list}.length - ${at2}`; + } + + code = `${list}.slice(${at1}, ${at2})`; + } else { + const at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + const at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + const wherePascalCase = { + FROM_START: 'FromStart', + FROM_END : 'FromEnd', + }; + const getIndex = (listName, where, at) => (where === 'FROM_END' ? `${listName}.length - 1 - ${at}` : at); + + const functionName = Blockly.JavaScript.provideFunction_( + `subsequence${wherePascalCase[where1]}${wherePascalCase[where2]}`, + [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence, at1, at2) { + var start = ${getIndex('sequence', where1, 'at1')}; + var end = ${getIndex('sequence', where2, 'at2')}; + return sequence.slice(start, end); + }`, + ] + ); + + code = `${functionName}(${list}, ${at1}, ${at2})`; + } + + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_indexOf.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_indexOf.js new file mode 100644 index 0000000000..d0bdf49219 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_indexOf.js @@ -0,0 +1,43 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_indexOf = { + init() { + this.jsonInit({ + message0: translate('in list %1 find %2 occurence of item %3'), + args0 : [ + { + type: 'input_value', + name: 'VALUE', + }, + { + type : 'field_dropdown', + name : 'END', + options: [[translate('first'), 'FIRST'], [translate('last'), 'LAST']], + }, + { + type: 'input_value', + name: 'FIND', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.lists_indexOf = block => { + const operator = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; + const item = Blockly.JavaScript.valueToCode(block, 'FIND', Blockly.JavaScript.ORDER_NONE) || '\'\''; + const list = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + + const code = `${list}.${operator}(${item})`; + + if (block.workspace.options.oneBasedIndex) { + return [`${code} + 1`, Blockly.JavaScript.ORDER_ADDITION]; + } + + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_isEmpty.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_isEmpty.js new file mode 100644 index 0000000000..83ad210d94 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_isEmpty.js @@ -0,0 +1,27 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_isEmpty = { + init() { + this.jsonInit({ + message0: translate('%1 is empty'), + args0 : [ + { + type: 'input_value', + name: 'VALUE', + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.lists_isEmpty = block => { + const list = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '[]'; + + const code = `!${list}.length`; + return [code, Blockly.JavaScript.ORDER_LOGICAL_NOT]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_length.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_length.js new file mode 100644 index 0000000000..6cdfdf0c27 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_length.js @@ -0,0 +1,27 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_length = { + init() { + this.jsonInit({ + message0: translate('length of %1'), + args0 : [ + { + type: 'input_value', + name: 'VALUE', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.lists_length = block => { + const list = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '[]'; + + const code = `${list}.length`; + return [code, Blockly.JavaScript.ORDER_MEMBER]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_repeat.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_repeat.js new file mode 100644 index 0000000000..2809e67b4f --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_repeat.js @@ -0,0 +1,51 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_repeat = { + init() { + this.jsonInit({ + message0: translate('set %1 to item %2 repeated %3 times'), + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: translate('list'), + }, + { + type: 'input_value', + name: 'ITEM', + }, + { + type: 'input_value', + name: 'NUM', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, +}; + +Blockly.JavaScript.lists_repeat = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const functionName = Blockly.JavaScript.provideFunction_('listsRepeat', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(value, n) { + var array = []; + for (var i = 0; i < n; i++) { + array[i] = value; + } + return array; + }`, + ]); + + const element = Blockly.JavaScript.valueToCode(block, 'ITEM', Blockly.JavaScript.ORDER_COMMA) || 'null'; + const repeatCount = Blockly.JavaScript.valueToCode(block, 'NUM', Blockly.JavaScript.ORDER_COMMA) || '0'; + + const code = `${varName} = ${functionName}(${element}, ${repeatCount});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_setIndex.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_setIndex.js new file mode 100644 index 0000000000..d54e9d3fd6 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_setIndex.js @@ -0,0 +1,132 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_setIndex = { + init() { + this.MODE_OPTIONS = [[translate('set'), 'SET'], [translate('insert at'), 'INSERT']]; + this.WHERE_OPTIONS = [ + [translate('#'), 'FROM_START'], + [translate('# from end'), 'FROM_END'], + [translate('first'), 'FIRST'], + [translate('last'), 'LAST'], + [translate('random'), 'RANDOM'], + ]; + + this.appendValueInput('LIST') + .setCheck('Array') + .appendField(translate('in list')); + this.appendDummyInput().appendField(new Blockly.FieldDropdown(this.MODE_OPTIONS), 'MODE'); + this.appendDummyInput('AT'); + this.appendValueInput('TO').appendField(translate('as')); + + this.setColourFromRawValues_( + Blockly.Colours.Binary.colour, + Blockly.Colours.Binary.colourSecondary, + Blockly.Colours.Binary.colourTertiary + ); + this.setPreviousStatement(true, null); + this.setNextStatement(true, null); + + this.updateAt_(true); + }, + mutationToDom() { + const container = document.createElement('mutation'); + const isAt = this.getInput('AT').type === Blockly.INPUT_VALUE; + + container.setAttribute('at', isAt); + return container; + }, + domToMutation(xmlElement) { + const isAt = xmlElement.getAttribute('at') !== 'false'; + this.updateAt_(isAt); + }, + updateAt_(isAt) { + this.removeInput('AT', true); + + if (isAt) { + this.appendValueInput('AT').setCheck('Number'); + } else { + this.appendDummyInput('AT'); + } + + const menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, value => { + const newAt = ['FROM_START', 'FROM_END'].includes(value); + if (newAt !== isAt) { + this.updateAt_(newAt); + this.setFieldValue(value, 'WHERE'); + return null; + } + return undefined; + }); + + this.moveInputBefore('AT', 'TO'); + this.getInput('AT').appendField(menu, 'WHERE'); + this.initSvg(); + this.render(false); + }, +}; + +Blockly.JavaScript.lists_setIndex = block => { + const mode = block.getFieldValue('MODE') || 'SET'; + const where = block.getFieldValue('WHERE') || 'FIRST'; + const value = Blockly.JavaScript.valueToCode(block, 'TO', Blockly.JavaScript.ORDER_ASSIGNMENT) || 'null'; + + let list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_MEMBER) || '[]'; + + const cacheList = () => { + if (list.match(/^\w+$/)) { + return ''; + } + + const listVar = Blockly.JavaScript.variableDB_.getDistinctName('tmpList', Blockly.Variables.NAME_TYPE); + const code = `var ${listVar} = ${list};\n`; + + list = listVar; + return code; + }; + + let code; + + if (where === 'FIRST') { + if (mode === 'SET') { + code = `list[0] = ${value};\n`; + } else if (mode === 'INSERT') { + code = `${list}.unshift(${value});\n`; + } + } else if (where === 'LAST') { + if (mode === 'SET') { + code = cacheList(); + code += `${list}[${list}.length - 1] = ${value};\n`; + } else if (mode === 'INSERT') { + code = `${list}.push(${value});\n`; + } + } else if (where === 'FROM_START') { + const at = Blockly.JavaScript.getAdjusted(block, 'AT'); + if (mode === 'SET') { + code = `${list}[${at}] = ${value};\n`; + } else if (mode === 'INSERT') { + code = `${list}.splice(${at}, 0, ${value});\n`; + } + } else if (where === 'FROM_END') { + const at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, false, Blockly.JavaScript.ORDER_SUBTRACTION); + code = cacheList(); + if (mode === 'SET') { + code = `${list}[${list}.length - ${at}] = ${value};\n`; + } else if (mode === 'INSERT') { + code = `${list}.splice(${list}.length - ${at}, 0, ${value});\n`; + } + } else if (where === 'RANDOM') { + code = cacheList(); + + const xVar = Blockly.JavaScript.variableDB_.getDistinctName('tmpX', Blockly.Variables.NAME_TYPE); + + code += `var ${xVar} = Math.floor(Math.random() * ${list}.length);\n`; + + if (mode === 'SET') { + code += `${list}[${xVar}] = ${value};\n`; + } else if (mode === 'INSERT') { + code += `${list}.splice(${xVar}, 0, ${value});\n`; + } + } + + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_sort.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_sort.js new file mode 100644 index 0000000000..064b25a20e --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_sort.js @@ -0,0 +1,62 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_sort = { + init() { + this.jsonInit({ + message0: translate('sort %1 %2 %3'), + args0 : [ + { + type : 'field_dropdown', + name : 'TYPE', + options: [ + [translate('numeric'), 'NUMERIC'], + [translate('alphabetic'), 'TEXT'], + [translate('alphabetic, ignore case'), 'IGNORE_CASE'], + ], + }, + { + type : 'field_dropdown', + name : 'DIRECTION', + options: [[translate('ascending'), 'ASCENDING'], [translate('descending'), 'DESCENDING']], + }, + { + type: 'input_value', + name: 'LIST', + }, + ], + output : 'Array', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.lists_sort = block => { + const list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '[]'; + const direction = block.getFieldValue('DIRECTION') === 'ASCENDING' ? 1 : -1; + const type = block.getFieldValue('TYPE'); + const getCompareFunctionName = Blockly.JavaScript.provideFunction_('listsGetSortCompare', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(type, direction) { + var compareFuncs = { + "NUMERIC": function(a, b) { + return parseFloat(a) - parseFloat(b); + }, + "TEXT": function(a, b) { + return a.toString() > b.toString() ? 1 : -1; + }, + "IGNORE_CASE": function(a, b) { + return a.toString().toLowerCase() > b.toString().toLowerCase() ? 1 : -1; + } + }; + var compare = compareFuncs[type]; + return function(a, b) { + return compare(a, b) * direction; + } + }`, + ]); + + const code = `${list}.slice().sort(${getCompareFunctionName}('${type}', ${direction}))`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_split.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_split.js new file mode 100644 index 0000000000..9a7bc25cdf --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_split.js @@ -0,0 +1,75 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lists_split = { + init() { + const dropdown = new Blockly.FieldDropdown( + [[translate('make list from text'), 'SPLIT'], [translate('make text from list'), 'JOIN']], + newMode => this.updateType_(newMode) + ); + + this.appendValueInput('INPUT') + .setCheck('String') + .appendField(dropdown, 'MODE'); + this.appendValueInput('DELIM') + .setCheck('String') + .appendField('', 'SPACE1') + .appendField(translate('with delimiter'), 'DELIM_LABEL'); + this.appendDummyInput().appendField('', 'SPACE2'); + + this.setOutput(true, 'Array'); + this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); + this.setColourFromRawValues_( + Blockly.Colours.Binary.colour, + Blockly.Colours.Binary.colourSecondary, + Blockly.Colours.Binary.colourTertiary + ); + }, + mutationToDom() { + const container = document.createElement('mutation'); + container.setAttribute('mode', this.getFieldValue('MODE')); + return container; + }, + domToMutation(xmlElement) { + this.updateType_(xmlElement.getAttribute('mode')); + }, + updateType_(newMode) { + const delimInput = this.getInput('DELIM'); + const spaceField = this.getField('SPACE1'); + + if (newMode === 'SPLIT') { + this.outputConnection.setCheck('Array'); + this.getInput('INPUT').setCheck('String'); + + // Create extra spacing for OUTPUT_SHAPE_SQUARE (i.e. string shapes) + if (!spaceField) { + delimInput.insertFieldAt(0, '', 'SPACE1'); + } + } else { + this.outputConnection.setCheck('String'); + this.getInput('INPUT').setCheck(null); + + if (spaceField) { + delimInput.removeField('SPACE1'); + } + } + + this.initSvg(); + this.render(false); + }, +}; + +Blockly.JavaScript.lists_split = block => { + const input = Blockly.JavaScript.valueToCode(block, 'INPUT', Blockly.JavaScript.ORDER_MEMBER); + const delimiter = Blockly.JavaScript.valueToCode(block, 'DELIM', Blockly.JavaScript.ORDER_NONE) || '\'\''; + const mode = block.getFieldValue('MODE'); + + let code; + + if (mode === 'SPLIT') { + code = `${input || '\'\''}.split(${delimiter})`; + } else if (mode === 'JOIN') { + code = `${input || '[]'}.join(${delimiter})`; + } + + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_statement.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_statement.js new file mode 100644 index 0000000000..fe5c65479a --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_statement.js @@ -0,0 +1,51 @@ +import { minusIconDark } from '../../../images'; + +Blockly.Blocks.lists_statement = { + init() { + this.jsonInit({ + message0: '%1', + args0 : [ + { + type: 'input_value', + name: 'VALUE', + }, + ], + nextStatement : 'text_statement', + previousStatement: 'text_statement', + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + // Render a ➖-icon for removing self + const fieldImage = new Blockly.FieldImage(minusIconDark, 25, 25, '', () => this.onIconClick()); + this.appendDummyInput('REMOVE_ICON').appendField(fieldImage); + }, + onIconClick() { + if (!this.workspace || this.isInFlyout) { + return; + } + + this.unplug(true); + this.dispose(); + }, + onchange() { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + // Self-destruct if located outside `lists_create_with` + const surroundParent = this.getSurroundParent(); + if (!surroundParent || surroundParent.type !== 'lists_create_with') { + this.unplug(true); + this.dispose(); + } + }, +}; + +Blockly.JavaScript.lists_statement = block => { + const code = Blockly.JavaScript.valueToCode(block, 'VALUE') || 'null'; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_flow_statements.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_flow_statements.js new file mode 100644 index 0000000000..a2476298c1 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_flow_statements.js @@ -0,0 +1,33 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.controls_flow_statements = { + init() { + this.jsonInit({ + message0: '%1', + args0 : [ + { + type : 'field_dropdown', + name : 'FLOW', + options: [ + [translate('break out'), 'BREAK'], + [translate('continue with next iteration'), 'CONTINUE'], + ], + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, +}; + +Blockly.JavaScript.controls_flow_statements = block => { + // Flow statements: continue, break. + if (block.getFieldValue('FLOW') === 'BREAK') { + return 'break;\n'; + } else if (block.getFieldValue('FLOW') === 'CONTINUE') { + return 'continue;\n'; + } +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js new file mode 100644 index 0000000000..c39163a0c5 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js @@ -0,0 +1,105 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.controls_for = { + init() { + this.jsonInit({ + message0: translate('count with %1 from %2 to %3 by %4'), + args0 : [ + { + type : 'field_variable', + name : 'VAR', + variable: null, + }, + { + type : 'input_value', + name : 'FROM', + check: 'Number', + }, + { + type : 'input_value', + name : 'TO', + check: 'Number', + }, + { + type : 'input_value', + name : 'BY', + check: 'Number', + }, + ], + message1: translate('do %1'), + args1 : [ + { + type: 'input_statement', + name: 'DO', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStaement: null, + nextStatement : null, + }); + }, +}; + +Blockly.JavaScript.controls_for = block => { + const variable0 = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + const argument0 = Blockly.JavaScript.valueToCode(block, 'FROM', Blockly.JavaScript.ORDER_ASSIGNMENT) || '0'; + const argument1 = Blockly.JavaScript.valueToCode(block, 'TO', Blockly.JavaScript.ORDER_ASSIGNMENT) || '0'; + const increment = Blockly.JavaScript.valueToCode(block, 'BY', Blockly.JavaScript.ORDER_ASSIGNMENT) || '1'; + + let branch = Blockly.JavaScript.statementToCode(block, 'DO'); + branch = Blockly.JavaScript.addLoopTrap(branch, block.id); + + let code = ''; + + if (Blockly.isNumber(argument0) && Blockly.isNumber(argument1) && Blockly.isNumber(increment)) { + const up = parseFloat(argument0) <= parseFloat(argument1); + const operator = up ? '<=' : '>='; + const step = Math.abs(parseFloat(increment)); + + const assignment = `${variable0} = ${argument0}`; + const condition = `${variable0} ${operator} ${argument1}`; + const statement = `${variable0} ${up ? '+=' : '-='} ${step}`; + + code = ` + for (${assignment}; ${condition}; ${statement}) { + ${branch} + }\n`; + } else { + // Cache non-trivial values to variables to prevent repeated look-ups. + let startVar = argument0; + if (!argument0.match(/^\w+$/) && !Blockly.isNumber(argument0)) { + startVar = Blockly.JavaScript.variableDB_.getDistinctName( + `${variable0}_start`, + Blockly.Variables.NAME_TYPE + ); + code = `var ${startVar} = ${argument0};\n`; + } + + let endVar = argument1; + if (!argument1.match(/^\w+$/) && !Blockly.isNumber(argument1)) { + endVar = Blockly.JavaScript.variableDB_.getDistinctName(`${variable0}_end`, Blockly.Variables.NAME_TYPE); + code += `var ${endVar} = ${argument1};\n`; + } + + // Determine loop direction at start, in case one of the bounds changes during loop execution. + const incVar = Blockly.JavaScript.variableDB_.getDistinctName(`${variable0}_inc`, Blockly.Variables.NAME_TYPE); + const incVal = Blockly.isNumber(increment) ? Math.abs(increment) : `Math.abs(${increment})`; + + code += ` + var ${incVar} = ${incVal}; + if (${startVar} > ${endVar}) { + ${incVar} = -${incVar}; + } + for ( + ${variable0} = ${startVar}; + ${incVar} >= 0 ? ${variable0} <= ${endVar} : ${variable0} >= ${endVar}; + ${variable0} += ${incVar} + ) { + ${branch}; + }\n`; + } + + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js new file mode 100644 index 0000000000..8749124a7c --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js @@ -0,0 +1,60 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.controls_forEach = { + init() { + this.jsonInit({ + message0: translate('for each item %1 in list %2'), + args0 : [ + { + type : 'field_variable', + name : 'VAR', + variable: null, + }, + { + type : 'input_value', + name : 'LIST', + check: 'Array', + }, + ], + message1: translate('do %1'), + args1 : [ + { + type: 'input_statement', + name: 'DO', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStaement: null, + nextStatement : null, + }); + }, +}; + +Blockly.JavaScript.controls_forEach = block => { + const variable0 = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + const argument0 = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_ASSIGNMENT) || '[]'; + + let branch = Blockly.JavaScript.statementToCode(block, 'DO'); + branch = Blockly.JavaScript.addLoopTrap(branch, block.id); + + let code = ''; + + // Cache non-trivial values to variables to prevent repeated look-ups. + let listVar = argument0; + if (!argument0.match(/^\w+$/)) { + listVar = Blockly.JavaScript.variableDB_.getDistinctName(`${variable0}_list`, Blockly.Variables.NAME_TYPE); + code = `var ${listVar} = ${argument0};\n`; + } + + const indexVar = Blockly.JavaScript.variableDB_.getDistinctName(`${variable0}_list`, Blockly.Variables.NAME_TYPE); + + code += ` + for (var ${indexVar} in ${listVar}) { + ${variable0} = ${listVar}[${indexVar}]; + ${branch} + }\n`; + + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat.js new file mode 100644 index 0000000000..3bdbd4226a --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat.js @@ -0,0 +1,31 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.controls_repeat = { + init() { + this.jsonInit({ + type : 'controls_repeat', + message0: translate('repeat %1 times'), + args0 : [ + { + type : 'field_number', + name : 'TIMES', + value : 10, + min : 0, + precision: 1, + }, + ], + message1: translate('do %1'), + args1 : [ + { + type: 'input_statement', + name: 'DO', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat_ext.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat_ext.js new file mode 100644 index 0000000000..6aa1e640e3 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat_ext.js @@ -0,0 +1,57 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.controls_repeat_ext = { + init() { + this.jsonInit({ + message0: translate('repeat %1 times'), + args0 : [ + { + type : 'input_value', + name : 'TIMES', + check: 'Number', + }, + ], + message1: translate('do %1'), + args1 : [ + { + type: 'input_statement', + name: 'DO', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, +}; + +Blockly.JavaScript.controls_repeat_ext = block => { + let repeats; + if (block.getField('TIMES')) { + repeats = String(Number(block.getFieldValue('TIMES'))); + } else { + repeats = Blockly.JavaScript.valueToCode(block, 'TIMES') || '0'; + } + + let branch = Blockly.JavaScript.statementToCode(block, 'DO'); + branch = Blockly.JavaScript.addLoopTrap(branch, block.id); + + let code = ''; + const loopVar = Blockly.JavaScript.variableDB_.getDistinctName('count', Blockly.Variables.NAME_TYPE); + let endVar = repeats; + + if (!repeats.match(/^\w+$/) && !Blockly.isNumber(repeats)) { + endVar = Blockly.JavaScript.variableDB_.getDistinctName('repeat_end', Blockly.Variables.NAME_TYPE); + code += `var ${endVar} = ${repeats}\n`; + } + + code += ` + for (var ${loopVar} = 0; ${loopVar} < ${endVar}; ${loopVar}++) { + ${branch} + }\n`; + return code; +}; + +Blockly.JavaScript.controls_repeat = Blockly.JavaScript.controls_repeat_ext; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_whileUntil.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_whileUntil.js new file mode 100644 index 0000000000..ec8ca03a0d --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_whileUntil.js @@ -0,0 +1,50 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.controls_whileUntil = { + init() { + this.jsonInit({ + message0: translate('repeat %1 %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'MODE', + options: [[translate('while'), 'WHILE'], [translate('until'), 'until']], + }, + { + type : 'input_value', + name : 'BOOL', + check: 'Boolean', + }, + ], + message1: translate('do %1'), + args1 : [ + { + type: 'input_statement', + name: 'DO', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, +}; + +Blockly.JavaScript.controls_whileUntil = block => { + const until = block.getFieldValue('MODE') === 'UNTIL'; + const order = until ? Blockly.JavaScript.ORDER_LOGICAL_NOT : Blockly.JavaScript.ORDER_NONE; + + let argument0 = Blockly.JavaScript.valueToCode(block, 'BOOL', order) || 'false'; + let branch = Blockly.JavaScript.statementToCode(block, 'DO'); + branch = Blockly.JavaScript.addLoopTrap(branch, block.id); + + if (until) { + argument0 = `!${argument0}`; + } + + return `while (${argument0}) { + ${branch} + }\n`; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/index.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/index.js new file mode 100644 index 0000000000..4510afa809 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/index.js @@ -0,0 +1,6 @@ +import './controls_repeat_ext'; +import './controls_repeat'; +import './controls_whileUntil'; +import './controls_for'; +import './controls_forEach'; +import './controls_flow_statements'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/index.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/index.js new file mode 100644 index 0000000000..277b3ac15c --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/index.js @@ -0,0 +1,2 @@ +import './variables_get'; +import './variables_set'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_get.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_get.js new file mode 100644 index 0000000000..0e1d1d95e6 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_get.js @@ -0,0 +1,28 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.variables_get = { + init() { + this.jsonInit({ + type : 'variables_get', + message0: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VAR', + variable: translate('item'), + }, + ], + output : null, + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : '', + }); + }, +}; + +Blockly.JavaScript.variables_get = block => { + const code = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_set.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_set.js new file mode 100644 index 0000000000..244fc79f1b --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_set.js @@ -0,0 +1,35 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.variables_set = { + init() { + this.jsonInit({ + type : 'field_variable', + message0: translate('set %1 to %2'), + args0 : [ + { + type : 'field_variable', + name : 'VAR', + variable: translate('item'), + }, + { + type: 'input_value', + name: 'VALUE', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + tooltip : '', + }); + }, +}; + +Blockly.JavaScript.variables_set = block => { + const argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ASSIGNMENT) || '0'; + const varName = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + + const code = `${varName} = ${argument0};\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/after_purchase.js b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/after_purchase.js new file mode 100644 index 0000000000..a11229a507 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/after_purchase.js @@ -0,0 +1,64 @@ +import { finishSign } from '../../../images'; +import { setBlockTextColor } from '../../../../utils'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.after_purchase = { + init() { + this.jsonInit({ + message0: translate('%1 (4) Get your trade result and trade again %2'), + message1: '%1', + args0 : [ + { + type : 'field_image', + src : finishSign, + width : 25, + height: 25, + alt : 'F', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'AFTERPURCHASE_STACK', + check: 'TradeAgain', + }, + ], + colour : '#2a3052', + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate( + 'Get the previous trade information and result, then trade again (Runs on trade finish)' + ), + }); + }, + onchange(event) { + setBlockTextColor(this); + if (!this.workspace || this.isInFlyout) { + return; + } + + // Maintain single instance of this block + if (event.type === Blockly.Events.BLOCK_CREATE) { + if (event.ids && event.ids.includes(this.id)) { + this.workspace.getAllBlocks(true).forEach(block => { + if (block.type === this.type && block.id !== this.id) { + block.dispose(); + } + }); + } + } + }, +}; + +Blockly.JavaScript.after_purchase = block => { + const stack = Blockly.JavaScript.statementToCode(block, 'AFTERPURCHASE_STACK'); + const code = ` + BinaryBotPrivateAfterPurchase = function BinaryBotPrivateAfterPurchase() { + ${stack} + return false; + };`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/check_result.js b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/check_result.js new file mode 100644 index 0000000000..1bfdf33868 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/check_result.js @@ -0,0 +1,46 @@ +import { insideAfterPurchase } from '../../../../relationChecker'; +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.contract_check_result = { + init() { + this.jsonInit({ + message0: translate('Result is %1'), + args0 : [ + { + type : 'field_dropdown', + name : 'CHECK_RESULT', + options: config.lists.CHECK_RESULT, + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('True if the result matches the selection'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('after_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.contract_check_result = block => { + const checkWith = block.getFieldValue('CHECK_RESULT'); + + const code = `Bot.isResult('${checkWith}')`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/index.js new file mode 100644 index 0000000000..9d47e3dbe3 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/index.js @@ -0,0 +1,4 @@ +import './after_purchase'; +import './check_result'; +import './read_details'; +import './trade_again'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/read_details.js b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/read_details.js new file mode 100644 index 0000000000..1d8b7f5568 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/read_details.js @@ -0,0 +1,45 @@ +import { insideAfterPurchase } from '../../../../relationChecker'; +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.read_details = { + init() { + this.jsonInit({ + message0: translate('Contract Detail: %1'), + args0 : [ + { + type : 'field_dropdown', + name : 'DETAIL_INDEX', + options: config.lists.DETAILS, + }, + ], + output : null, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Reads a selected option from contract details list'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('after_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.read_details = block => { + const detailIndex = block.getFieldValue('DETAIL_INDEX'); + + const code = `Bot.readDetails(${detailIndex})`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/trade_again.js b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/trade_again.js new file mode 100644 index 0000000000..6bc7bcc41f --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/trade_again.js @@ -0,0 +1,34 @@ +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.trade_again = { + init() { + this.jsonInit({ + message0 : translate('Trade Again'), + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + tooltip : translate('Runs the trade block again'), + }); + + // Ensure one of this type per statement-stack + this.setNextStatement(false); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('after_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.trade_again = () => 'return true;\n'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/ask_price.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/ask_price.js new file mode 100644 index 0000000000..9f21b60ae8 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/ask_price.js @@ -0,0 +1,46 @@ +import { insideBeforePurchase } from '../../../../relationChecker'; +import { getPurchaseChoices } from '../../../shared'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.ask_price = { + init() { + this.jsonInit({ + message0: translate('Ask Price %1'), + args0 : [ + { + type : 'field_dropdown', + name : 'PURCHASE_LIST', + options: getPurchaseChoices, + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Ask Price for selected proposal'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('before_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.ask_price = block => { + const purchaseList = block.getFieldValue('PURCHASE_LIST'); + + const code = `Bot.getAskPrice('${purchaseList}')`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/before_purchase.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/before_purchase.js new file mode 100644 index 0000000000..66735e7385 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/before_purchase.js @@ -0,0 +1,61 @@ +import { purchase } from '../../../images'; +import { setBlockTextColor } from '../../../../utils'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.before_purchase = { + init() { + this.jsonInit({ + message0: translate('%1 (2) Watch and purchase your contract %2'), + message1: '%1', + args0 : [ + { + type : 'field_image', + src : purchase, + width : 25, + height: 25, + alt : 'P', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'BEFOREPURCHASE_STACK', + check: 'Purchase', + }, + ], + colour : '#2a3052', + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Watch the tick stream and purchase the desired contract (Runs on tick update)'), + }); + }, + onchange(event) { + setBlockTextColor(this); + if (!this.workspace || this.isInFlyout) { + return; + } + + // Maintain single instance of this block + if (event.type === Blockly.Events.BLOCK_CREATE) { + if (event.ids && event.ids.includes(this.id)) { + this.workspace.getAllBlocks(true).forEach(block => { + if (block.type === this.type && block.id !== this.id) { + block.dispose(); + } + }); + } + } + }, +}; + +Blockly.JavaScript.before_purchase = block => { + const stack = Blockly.JavaScript.statementToCode(block, 'BEFOREPURCHASE_STACK'); + + const code = `BinaryBotPrivateBeforePurchase = function BinaryBotPrivateBeforePurchase() { + ${stack} + };\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/index.js new file mode 100644 index 0000000000..78e92d0a92 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/index.js @@ -0,0 +1,4 @@ +import './before_purchase'; +import './purchase'; +import './ask_price'; +import './payout'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/payout.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/payout.js new file mode 100644 index 0000000000..c772b98398 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/payout.js @@ -0,0 +1,45 @@ +import { getPurchaseChoices } from '../../../shared'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.payout = { + init() { + this.jsonInit({ + message0: translate('Payout %1'), + args0 : [ + { + type : 'field_dropdown', + name : 'PURCHASE_LIST', + options: getPurchaseChoices, + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Payout for selected proposal'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('before_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.payout = block => { + const purchaseList = block.getFieldValue('PURCHASE_LIST'); + + const code = `Bot.getPayout('${purchaseList}')`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/purchase.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/purchase.js new file mode 100644 index 0000000000..5af531d642 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/purchase.js @@ -0,0 +1,70 @@ +import { insideBeforePurchase } from '../../../../relationChecker'; +import { getPurchaseChoices, updatePurchaseChoices } from '../../../shared'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.purchase = { + init() { + this.jsonInit({ + message0: translate('Purchase %1'), + args0 : [ + { + type : 'field_dropdown', + name : 'PURCHASE_LIST', + options: getPurchaseChoices, + }, + ], + previousStatement: null, + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Payout for selected proposal'), + }); + + // Ensure one of this type per statement-stack + this.setNextStatement(false); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('before_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } else if (event.type === Blockly.Events.BLOCK_CHANGE || Blockly.Events.BLOCK_CREATE) { + const tradeDefinitionBlock = this.workspace + .getAllBlocks(true) + .find(block => block.type === 'trade_definition'); + + if (!tradeDefinitionBlock) { + return; + } + + const tradeTypeBlock = tradeDefinitionBlock.getChildByType('trade_definition_tradetype'); + if (!tradeTypeBlock) { + return; + } + + const tradeType = tradeTypeBlock.getFieldValue('TRADETYPE_LIST'); + const contractTypeBlock = tradeDefinitionBlock.getChildByType('trade_definition_contracttype'); + const contractType = contractTypeBlock.getFieldValue('TYPE_LIST'); + const oppositesName = tradeType.toUpperCase(); + + if (tradeType && contractType && oppositesName) { + updatePurchaseChoices(contractType, oppositesName); + } + } + }, +}; + +Blockly.JavaScript.purchase = block => { + const purchaseList = block.getFieldValue('PURCHASE_LIST'); + + const code = `Bot.purchase('${purchaseList}');\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/check_sell.js b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/check_sell.js new file mode 100644 index 0000000000..c6deda731c --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/check_sell.js @@ -0,0 +1,36 @@ +import { insideDuringPurchase } from '../../../../relationChecker'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.check_sell = { + init() { + this.jsonInit({ + message0 : translate('Sell is available'), + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('True if sell at market is available'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('during_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.check_sell = () => { + const code = 'Bot.isSellAvailable()'; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/during_purchase.js b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/during_purchase.js new file mode 100644 index 0000000000..844bc70ee1 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/during_purchase.js @@ -0,0 +1,63 @@ +import { sellContract } from '../../../images'; +import { setBlockTextColor } from '../../../../utils'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.during_purchase = { + init() { + this.jsonInit({ + message0: translate('%1 (3) Watch and sell your purchased contract %2'), + message1: '%1', + args0 : [ + { + type : 'field_image', + src : sellContract, + width : 25, + height: 25, + alt : 'S', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'DURING_PURCHASE_STACK', + check: 'SellAtMarket', + }, + ], + colour : '#2a3052', + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate( + 'Watch the purchased contract info and sell at market if available (Runs on contract update)' + ), + }); + }, + onchange(event) { + setBlockTextColor(this); + if (!this.workspace || this.isInFlyout) { + return; + } + + // Maintain single instance of this block + if (event.type === Blockly.Events.BLOCK_CREATE) { + if (event.ids && event.ids.includes(this.id)) { + this.workspace.getAllBlocks(true).forEach(block => { + if (block.type === this.type && block.id !== this.id) { + block.dispose(); + } + }); + } + } + }, +}; + +Blockly.JavaScript.during_purchase = block => { + const stack = Blockly.JavaScript.statementToCode(block, 'DURING_PURCHASE_STACK'); + + const code = `BinaryBotPrivateDuringPurchase = function BinaryBotPrivateDuringPurchase() { + ${stack} + };\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/index.js new file mode 100644 index 0000000000..66059bd23d --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/index.js @@ -0,0 +1,4 @@ +import './during_purchase'; +import './sell_at_market'; +import './check_sell'; +import './sell_price'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_at_market.js b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_at_market.js new file mode 100644 index 0000000000..5f180c56a3 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_at_market.js @@ -0,0 +1,33 @@ +import { insideDuringPurchase } from '../../../../relationChecker'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.sell_at_market = { + init() { + this.jsonInit({ + message0 : translate('Sell at market'), + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + tooltip : translate('Sell at market'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('during_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.sell_at_market = () => 'Bot.sellAtMarket();\n'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_price.js b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_price.js new file mode 100644 index 0000000000..2796658a97 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_price.js @@ -0,0 +1,35 @@ +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.sell_price = { + init() { + this.jsonInit({ + message0 : translate('Sell profit/loss'), + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the profit for sell at market.'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + if (this.isDescendantOf('during_purchase')) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.sell_price = () => { + const code = 'Bot.getSellPrice()'; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/fast_ema_period.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/fast_ema_period.js new file mode 100644 index 0000000000..798861585e --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/fast_ema_period.js @@ -0,0 +1,28 @@ +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.fast_ema_period = { + init() { + this.jsonInit({ + message0: translate('Fast EMA Period %1'), + args0 : [ + { + type : 'input_value', + name : 'FAST_EMA_PERIOD', + check: null, + }, + ], + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange : Blockly.Blocks.input_list.onchange, + allowedParents: ['macda_statement'], +}; + +Blockly.JavaScript.fast_ema_period = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/index.js new file mode 100644 index 0000000000..b388963716 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/index.js @@ -0,0 +1,7 @@ +import './input_list'; +import './period'; +import './std_dev_multiplier_up'; +import './std_dev_multiplier_down'; +import './fast_ema_period'; +import './slow_ema_period'; +import './signal_ema_period'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/input_list.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/input_list.js new file mode 100644 index 0000000000..8349e18718 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/input_list.js @@ -0,0 +1,52 @@ +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.input_list = { + init() { + this.jsonInit({ + message0: translate('Input List %1'), + args0 : [ + { + type : 'input_value', + name : 'INPUT_LIST', + check: null, + }, + ], + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const surroundParent = this.getSurroundParent(); + if (!surroundParent || !this.allowedParents.includes(surroundParent.type)) { + Blockly.Events.disable(); + this.unplug(true); + this.dispose(); + Blockly.Events.enable(); + } + } + }, + allowedParents: [ + 'bb_statement', + 'bba_statement', + 'ema_statement', + 'emaa_statement', + 'macda_statement', + 'rsi_statement', + 'rsia_statement', + 'sma_statement', + 'smaa_statement', + ], +}; + +Blockly.JavaScript.input_list = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/period.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/period.js new file mode 100644 index 0000000000..25fd1b8fd8 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/period.js @@ -0,0 +1,38 @@ +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.period = { + init() { + this.jsonInit({ + message0: translate('Period %1'), + args0 : [ + { + type : 'input_value', + name : 'PERIOD', + check: null, + }, + ], + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange : Blockly.Blocks.input_list.onchange, + allowedParents: [ + 'bb_statement', + 'bba_statement', + 'ema_statement', + 'emaa_statement', + 'macda_statement', + 'rsi_statement', + 'rsia_statement', + 'sma_statement', + 'smaa_statement', + ], +}; + +Blockly.JavaScript.period = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/signal_ema_period.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/signal_ema_period.js new file mode 100644 index 0000000000..3e9fbeb538 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/signal_ema_period.js @@ -0,0 +1,28 @@ +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.signal_ema_period = { + init() { + this.jsonInit({ + message0: translate('Signal EMA Period %1'), + args0 : [ + { + type : 'input_value', + name : 'SIGNAL_EMA_PERIOD', + check: null, + }, + ], + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange : Blockly.Blocks.input_list.onchange, + allowedParents: ['macda_statement'], +}; + +Blockly.JavaScript.signal_ema_period = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/slow_ema_period.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/slow_ema_period.js new file mode 100644 index 0000000000..dd619b4f68 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/slow_ema_period.js @@ -0,0 +1,28 @@ +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.slow_ema_period = { + init() { + this.jsonInit({ + message0: translate('Slow EMA Period %1'), + args0 : [ + { + type : 'input_value', + name : 'SLOW_EMA_PERIOD', + check: null, + }, + ], + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange : Blockly.Blocks.input_list.onchange, + allowedParents: ['macda_statement'], +}; + +Blockly.JavaScript.slow_ema_period = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/std_dev_multiplier_down.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/std_dev_multiplier_down.js new file mode 100644 index 0000000000..5e102d302d --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/std_dev_multiplier_down.js @@ -0,0 +1,25 @@ +import { translate } from '../../../../../../../../common/i18n'; + +Blockly.Blocks.std_dev_multiplier_down = { + init() { + this.jsonInit({ + message0: translate('Standard Deviation Down Multiplier %1'), + args0 : [ + { + type : 'input_value', + name : 'DOWNMULTIPLIER', + check: null, + }, + ], + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, + onchange : Blockly.Blocks.input_list.onchange, + allowedParents: ['bb_statement', 'bba_statement'], +}; + +Blockly.JavaScript.std_dev_multiplier_down = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/std_dev_multiplier_up.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/std_dev_multiplier_up.js new file mode 100644 index 0000000000..90fe90be04 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/Parts/std_dev_multiplier_up.js @@ -0,0 +1,28 @@ +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.std_dev_multiplier_up = { + init() { + this.jsonInit({ + message0: translate('Standard Deviation Up Multiplier %1'), + args0 : [ + { + type : 'input_value', + name : 'UPMULTIPLIER', + check: null, + }, + ], + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange : Blockly.Blocks.input_list.onchange, + allowedParents: ['bb_statement', 'bba_statement'], +}; + +Blockly.JavaScript.std_dev_multiplier_up = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bb_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bb_statement.js new file mode 100644 index 0000000000..70368cf5bc --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bb_statement.js @@ -0,0 +1,90 @@ +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.bb_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to Bollinger Bands %2 %3'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'bb', + }, + { + type : 'field_dropdown', + name : 'BBRESULT_LIST', + options: config.bbResult, + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Calculates Bollinger Bands (BB) from a list with a period'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + // Only allow `indicator_parts` type blocks + const blocksInStatement = this.getBlocksInStatement('STATEMENT'); + blocksInStatement.forEach(block => { + if (this.requiredParamBlocks && !this.requiredParamBlocks.includes(block.type)) { + Blockly.Events.disable(); + block.unplug(); + Blockly.Events.enable(); + } + }); + + // Ensure indicator has required param blocks + const parametersToReadd = this.requiredParamBlocks.filter( + paramName => blocksInStatement.findIndex(block => block.type === paramName) === -1 + ); + + parametersToReadd.forEach(paramName => { + Blockly.Events.disable(); + + const paramBlock = this.workspace.newBlock(paramName); + paramBlock.initSvg(); + paramBlock.render(); + + const connection = this.getLastConnectionInStatement('STATEMENT'); + connection.connect(paramBlock.previousConnection); + + Blockly.Events.enable(); + }); + } + }, + requiredParamBlocks: ['input_list', 'period', 'std_dev_multiplier_up', 'std_dev_multiplier_down'], +}; + +Blockly.JavaScript.bb_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const bbResult = block.getFieldValue('BBRESULT_LIST'); + const input = block.childValueToCode('input_list', 'INPUT_LIST') || '[]'; + const period = block.childValueToCode('period', 'PERIOD') || '10'; + const stdDevUp = block.childValueToCode('std_dev_multiplier_up', 'UPMULTIPLIER') || '5'; + const stdDevDown = block.childValueToCode('std_dev_multiplier_down', 'DOWNMULTIPLIER') || '5'; + + const code = `${varName} = Bot.bb(${input}, { periods: ${period}, stdDevUp: ${stdDevUp}, stdDevDown: ${stdDevDown} }, ${bbResult});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bba_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bba_statement.js new file mode 100644 index 0000000000..f8d7b37b96 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bba_statement.js @@ -0,0 +1,56 @@ +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.bba_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to Bollinger Bands Array %2 %3'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'bb', + }, + { + type : 'field_dropdown', + name : 'BBRESULT_LIST', + options: config.bbResult, + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Calculates Bollinger Bands (BB) list from a list with a period'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange : Blockly.Blocks.bb_statement.onchange, + requiredParamBlocks: ['input_list', 'period', 'std_dev_multiplier_up', 'std_dev_multiplier_down'], +}; + +Blockly.JavaScript.bba_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const bbResult = block.getFieldValue('BBRESULT_LIST'); + const input = block.getChildFieldValue('input_list', 'INPUT_LIST') || '[]'; + const period = block.getChildFieldValue('period', 'PERIOD') || '10'; + const stdDevUp = block.getChildFieldValue('std_dev_multiplier_up', 'UPMULTIPLIER') || '5'; + const stdDevDown = block.getChildFieldValue('std_dev_multiplier_down', 'DOWNMULTIPLIER') || '5'; + + const code = `${varName} = Bot.bba(${input}, { periods: ${period}, stdDevUp: ${stdDevUp}, stdDevDown: ${stdDevDown} }, ${bbResult});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/ema_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/ema_statement.js new file mode 100644 index 0000000000..c17ca64305 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/ema_statement.js @@ -0,0 +1,47 @@ +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.ema_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to Exponentional Moving Average %2'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'ema', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Calculates Exponential Moving Average (EMA) from a list with a period'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange : Blockly.Blocks.bb_statement.onchange, + requiredParamBlocks: ['input_list', 'period'], +}; + +Blockly.JavaScript.ema_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const input = block.childValueToCode('input_list', 'INPUT_LIST') || '[]'; + const period = block.childValueToCode('period', 'PERIOD') || '10'; + + const code = `${varName} = Bot.ema(${input}, ${period});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/emaa_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/emaa_statement.js new file mode 100644 index 0000000000..e9eed783ad --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/emaa_statement.js @@ -0,0 +1,47 @@ +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.emaa_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to Exponentional Moving Average Array %2'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'emaa', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Calculates Exponential Moving Average (EMA) list from a list of values with a period'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange : Blockly.Blocks.bb_statement.onchange, + requiredParamBlocks: ['input_list', 'period'], +}; + +Blockly.JavaScript.emaa_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const input = block.getChildFieldValue('input_list', 'INPUT_LIST') || '[]'; + const period = block.getChildFieldValue('period', 'PERIOD') || '10'; + + const code = `${varName} = Bot.emaa(${input}, ${period});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/index.js new file mode 100644 index 0000000000..ff62324e57 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/index.js @@ -0,0 +1,10 @@ +import './bb_statement'; +import './bba_statement'; +import './ema_statement'; +import './emaa_statement'; +import './rsi_statement'; +import './rsia_statement'; +import './sma_statement'; +import './smaa_statement'; +import './macda_statement'; +import './Parts'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/macda_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/macda_statement.js new file mode 100644 index 0000000000..d99386f4e7 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/macda_statement.js @@ -0,0 +1,60 @@ +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.macda_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to MACD Array %2 %3'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'bb', + }, + { + type : 'field_dropdown', + name : 'MACDFIELDS_LIST', + options: config.macdFields, + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Calculates Moving Average Convergence Divergence (MACD) list from a list'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange : Blockly.Blocks.bb_statement.onchange, + requiredParamBlocks: ['input_list', 'period', 'fast_ema_period', 'slow_ema_period', 'signal_ema_period'], +}; + +Blockly.JavaScript.macda_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const macdField = block.getFieldValue('MACDFIELDS_LIST'); + const input = block.getChildFieldValue('input_list', 'INPUT_LIST') || '[]'; + const fastEmaPeriod = block.getChildFieldValue('fast_ema_period', 'FAST_EMA_PERIOD') || '12'; + const slowEmaPeriod = block.getChildFieldValue('slow_ema_period', 'SLOW_EMA_PERIOD') || '26'; + const signalEmaPeriod = block.getChildFieldValue('signal_ema_period', 'SIGNAL_EMA_PERIOD') || '9'; + + const code = `${varName} = Bot.macda(${input}, { + fastEmaPeriod: ${fastEmaPeriod}, + slowEmaPeriod: ${slowEmaPeriod}, + signalEmaPeriod: ${signalEmaPeriod}, + }, ${macdField});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsi_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsi_statement.js new file mode 100644 index 0000000000..e32de5493a --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsi_statement.js @@ -0,0 +1,47 @@ +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.rsi_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to Relative Strength Index %2'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'rsi', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Relative Strength Index (RSI) from a list with a period'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange : Blockly.Blocks.bb_statement.onchange, + requiredParamBlocks: ['input_list', 'period'], +}; + +Blockly.JavaScript.rsi_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const input = block.childValueToCode('input_list', 'INPUT_LIST') || '[]'; + const period = block.childValueToCode('period', 'PERIOD') || '10'; + + const code = `${varName} = Bot.rsi(${input}, ${period});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsia_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsia_statement.js new file mode 100644 index 0000000000..5c72a1d11c --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsia_statement.js @@ -0,0 +1,47 @@ +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.rsia_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to Relative Strength Index Array %2'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'rsia', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Calculates Relative Strength Index (RSI) list from a list of values with a period'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange : Blockly.Blocks.bb_statement.onchange, + requiredParamBlocks: ['input_list', 'period'], +}; + +Blockly.JavaScript.rsia_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const input = block.getChildFieldValue('input_list', 'INPUT_LIST') || '[]'; + const period = block.getChildFieldValue('period', 'PERIOD') || '10'; + + const code = `${varName} = Bot.rsia(${input}, ${period});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/sma_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/sma_statement.js new file mode 100644 index 0000000000..f561be6495 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/sma_statement.js @@ -0,0 +1,47 @@ +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.sma_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to Simple Moving Average %2'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'sma', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Calculates Simple Moving Average (SMA) from a list with a period'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange : Blockly.Blocks.bb_statement.onchange, + requiredParamBlocks: ['input_list', 'period'], +}; + +Blockly.JavaScript.sma_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const input = block.getChildFieldValue('input_list', 'INPUT_LIST') || '[]'; + const period = block.getChildFieldValue('period', 'PERIOD') || '10'; + + const code = `${varName} = Bot.sma(${input}, ${period});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/smaa_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/smaa_statement.js new file mode 100644 index 0000000000..ce593225e4 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/smaa_statement.js @@ -0,0 +1,46 @@ +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.smaa_statement = { + init() { + this.jsonInit({ + message0: translate('set %1 to Simple Moving Average Array %2'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: 'smaa', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type : 'input_statement', + name : 'STATEMENT', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Calculates Simple Moving Average (SMA) from a list with a period'), + previousStatement: null, + nextStatement : null, + }); + }, + onchange: Blockly.Blocks.bb_statement.onchange, +}; + +Blockly.JavaScript.smaa_statement = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + const input = block.getChildFieldValue('input_list', 'INPUT_LIST') || '[]'; + const period = block.getChildFieldValue('period', 'PERIOD') || '10'; + + const code = `${varName} = Bot.smaa(${input}, ${period});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/check_direction.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/check_direction.js new file mode 100644 index 0000000000..7a8ea1217e --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/check_direction.js @@ -0,0 +1,52 @@ +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.check_direction = { + init() { + this.jsonInit({ + message0: translate('Direction is %1'), + args0 : [ + { + type : 'field_dropdown', + name : 'CHECK_DIRECTION', + options: config.lists.CHECK_DIRECTION, + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('True if the direction matches the selection'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.check_direction = block => { + const checkWith = block.getFieldValue('CHECK_DIRECTION'); + + const code = `Bot.checkDirection('${checkWith}')`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/get_ohlc.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/get_ohlc.js new file mode 100644 index 0000000000..d912563ac5 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/get_ohlc.js @@ -0,0 +1,62 @@ +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.get_ohlc = { + init() { + this.jsonInit({ + message0: translate('in candle list get # from end %1'), + message1: translate('with interval: %1'), + args0 : [ + { + type : 'input_value', + name : 'CANDLEINDEX', + check: 'Number', + }, + ], + args1: [ + { + type : 'field_dropdown', + name : 'CANDLEINTERVAL_LIST', + options: config.candleIntervals, + }, + ], + output : 'Candle', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Get the nth recent candle'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.get_ohlc = block => { + const selectedGranularity = block.getFieldValue('CANDLEINTERVAL_LIST'); + const granularity = selectedGranularity === 'default' ? 'undefined' : selectedGranularity; + const index = Blockly.JavaScript.valueToCode(block, 'CANDLEINDEX', Blockly.JavaScript.ORDER_ATOMIC) || '1'; + + const code = `Bot.getOhlcFromEnd({ index: ${index}, granularity: ${granularity} })`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/index.js new file mode 100644 index 0000000000..089bfcf922 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/index.js @@ -0,0 +1,10 @@ +import './tick'; +import './ticks'; +import './ohlc'; +import './ohlc_values'; +import './readOhlc'; +import './get_ohlc'; +import './check_direction'; +import './tick_analysis'; +import './last_digit'; +import './lastDigitList'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/lastDigitList.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/lastDigitList.js new file mode 100644 index 0000000000..14add8c188 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/lastDigitList.js @@ -0,0 +1,39 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.lastDigitList = { + init() { + this.jsonInit({ + message0 : translate('Last Digit List'), + output : 'Array', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the list of last digit values'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.lastDigitList = () => ['Bot.getLastDigitList()', Blockly.JavaScript.ORDER_ATOMIC]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/last_digit.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/last_digit.js new file mode 100644 index 0000000000..578df29b85 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/last_digit.js @@ -0,0 +1,39 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.last_digit = { + init() { + this.jsonInit({ + message0 : translate('Last Digit'), + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the last digit of the latest tick'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.last_digit = () => ['Bot.getLastDigit()', Blockly.JavaScript.ORDER_ATOMIC]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ohlc.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ohlc.js new file mode 100644 index 0000000000..3df7b3d682 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ohlc.js @@ -0,0 +1,54 @@ +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.ohlc = { + init() { + this.jsonInit({ + message0: translate('Candles List'), + message1: translate('with interval: %1'), + args1 : [ + { + type : 'field_dropdown', + name : 'CANDLEINTERVAL_LIST', + options: config.candleIntervals, + }, + ], + output : 'Array', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the candle list'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.ohlc = block => { + const selectedGranularity = block.getFieldValue('CANDLEINTERVAL_LIST'); + const granularity = selectedGranularity === 'default' ? 'undefined' : selectedGranularity; + + const code = `Bot.getOhlc({ granularity: ${granularity} })`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ohlc_values.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ohlc_values.js new file mode 100644 index 0000000000..ebae80cfd8 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ohlc_values.js @@ -0,0 +1,59 @@ +import { translate } from '../../../../../../../common/i18n'; +import config from '../../../../../../common/const'; + +Blockly.Blocks.ohlc_values = { + init() { + this.jsonInit({ + message0: translate('Make a List of %1 values in candles list with interval: %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'OHLCFIELD_LIST', + options: config.ohlcFields, + }, + { + type : 'field_dropdown', + name : 'CANDLEINTERVAL_LIST', + options: config.candleIntervals, + }, + ], + output : 'Array', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns a list of the selected candle values'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.ohlc_values = block => { + const selectedGranularity = block.getFieldValue('CANDLEINTERVAL_LIST'); + const granularity = selectedGranularity === 'default' ? 'undefined' : selectedGranularity; + const ohlcField = block.getFieldValue('OHLCFIELD_LIST'); + + const code = `Bot.getOhlc({ field: '${ohlcField}', granularity: ${granularity} })`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/readOhlc.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/readOhlc.js new file mode 100644 index 0000000000..d8bf706c6d --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/readOhlc.js @@ -0,0 +1,68 @@ +import { translate } from '../../../../../../../common/i18n'; +import config from '../../../../../../common/const'; + +Blockly.Blocks.read_ohlc = { + init() { + this.jsonInit({ + message0: translate('In candles list read %1 from end %2'), + message1: translate('with interval: %1'), + args0 : [ + { + type : 'field_dropdown', + name : 'OHLCFIELD_LIST', + options: config.ohlcFields, + }, + { + type : 'input_value', + name : 'CANDLEINDEX', + check: 'Number', + }, + ], + args1: [ + { + type : 'field_dropdown', + name : 'CANDLEINTERVAL_LIST', + options: config.candleIntervals, + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Read the selected candle value in the nth recent candle'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.read_ohlc = block => { + const selectedGranularity = block.getFieldValue('CANDLEINTERVAL_LIST'); + const granularity = selectedGranularity === 'default' ? 'undefined' : selectedGranularity; + const ohlcField = block.getFieldValue('OHLCFIELD_LIST'); + const index = Blockly.JavaScript.valueToCode(block, 'CANDLEINDEX', Blockly.JavaScript.ORDER_ATOMIC) || '1'; + + const code = `Bot.getOhlcFromEnd({ field: '${ohlcField}', index: ${index}, granularity: ${granularity} })`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/tick.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/tick.js new file mode 100644 index 0000000000..ea8f1e757e --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/tick.js @@ -0,0 +1,39 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.tick = { + init() { + this.jsonInit({ + message0 : translate('Last Tick'), + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the tick value received by a before purchase block'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.tick = () => ['Bot.getLastTick()', Blockly.JavaScript.ORDER_ATOMIC]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/tick_analysis.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/tick_analysis.js new file mode 100644 index 0000000000..9beb54584a --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/tick_analysis.js @@ -0,0 +1,33 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.tick_analysis = { + init() { + this.jsonInit({ + message0: translate('This block is called on every tick %1 %2'), + args0 : [ + { + type: 'input_dummy', + }, + { + type : 'input_statement', + name : 'TICKANALYSIS_STACK', + check: null, + }, + ], + colour : '#fef1cf', + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('You can use this block to analyze the ticks, regardless of your trades'), + }); + }, +}; + +Blockly.JavaScript.tick_analysis = block => { + const stack = Blockly.JavaScript.statementToCode(block, 'TICKANALYSIS_STACK') || ''; + + const code = ` + BinaryBotPrivateTickAnalysisList.push(function BinaryBotPrivateTickAnalysis() { + ${stack} + });\n`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ticks.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ticks.js new file mode 100644 index 0000000000..8658066a5d --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tick Analysis/ticks.js @@ -0,0 +1,39 @@ +import { translate } from '../../../../../../../common/i18n'; + +Blockly.Blocks.ticks = { + init() { + this.jsonInit({ + message0 : translate('Ticks List'), + output : 'Array', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the list of tick values'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.ticks = () => ['Bot.getTicks()', Blockly.JavaScript.ORDER_ATOMIC]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/index.js new file mode 100644 index 0000000000..80d5c9cbd2 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/index.js @@ -0,0 +1,3 @@ +import './is_candle_black'; +import './ohlc_values_in_list'; +import './read_ohlc_obj'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/is_candle_black.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/is_candle_black.js new file mode 100644 index 0000000000..d6fadc3871 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/is_candle_black.js @@ -0,0 +1,31 @@ +import { translate } from '../../../../../../../../common/i18n'; + +Blockly.Blocks.is_candle_black = { + init() { + this.jsonInit({ + message0: translate('Is candle black? %1'), + args0 : [ + { + type : 'input_value', + name : 'OHLCOBJ', + check: 'Candle', + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate( + 'Checks if the given candle is black, returns true if close is less than open in the given candle.' + ), + }); + }, +}; + +Blockly.JavaScript.is_candle_black = block => { + const ohlcObj = Blockly.JavaScript.valueToCode(block, 'OHLCOBJ') || '{}'; + + const code = `Bot.isCandleBlack(${ohlcObj})`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/ohlc_values_in_list.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/ohlc_values_in_list.js new file mode 100644 index 0000000000..8b60ae39f3 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/ohlc_values_in_list.js @@ -0,0 +1,36 @@ +import config from '../../../../../../../common/const'; +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.ohlc_values_in_list = { + init() { + this.jsonInit({ + message0: translate('Make a list of %1 values from candles list %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'OHLCFIELD_LIST', + options: config.ohlcFields, + }, + { + type : 'field_dropdown', + name : 'OHLCLIST', + options: config.candleIntervals, + }, + ], + output : 'Array', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns a list of the selected candle values'), + }); + }, +}; + +Blockly.JavaScript.ohlc_values_in_list = block => { + const ohlcList = Blockly.JavaScript.valueToCode(block, 'OHLCLIST') || '[]'; + const ohlcField = block.getFieldValue('OHLCFIELD_LIST'); + + const code = `Bot.candleValues(${ohlcList}, '${ohlcField}')`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/read_ohlc_obj.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/read_ohlc_obj.js new file mode 100644 index 0000000000..2c43f75a5b --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Candle/read_ohlc_obj.js @@ -0,0 +1,35 @@ +import config from '../../../../../../../common/const'; +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.read_ohlc_obj = { + init() { + this.jsonInit({ + message0: translate('Read %1 value in candle %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'OHLCFIELD_LIST', + options: config.ohlcFields, + }, + { + type: 'input_value', + name: 'OHLCOBJ', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Read a field in a candle (read from the Candles list)'), + }); + }, +}; + +Blockly.JavaScript.read_ohlc_obj = block => { + const ohlcField = block.getFieldValue('OHLCFIELD_LIST'); + const ohlcObj = Blockly.JavaScript.valueToCode(block, 'OHLCOBJ') || '{}'; + + const code = `Bot.candleField(${ohlcObj}, '${ohlcField}');`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./balance.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./balance.js new file mode 100644 index 0000000000..24f36f55bd --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./balance.js @@ -0,0 +1,43 @@ +import { translate } from '../../../../../../../../common/i18n'; +import config from '../../../../../../../common/const'; + +Blockly.Blocks.balance = { + init() { + this.jsonInit({ + message0: translate('Balance: %1'), + args0 : [ + { + type : 'field_dropdown', + name : 'BALANCE_TYPE', + options: config.lists.BALANCE_TYPE, + }, + ], + output : null, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + + // Change shape based on selected type + const balanceTypeField = this.getField('BALANCE_TYPE'); + balanceTypeField.setValidator(value => { + if (value === 'STR') { + this.setOutputShape(Blockly.OUTPUT_SHAPE_SQUARE); + this.setOutput(true, 'String'); + } else if (value === 'NUM') { + this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); + this.setOutput(true, 'Number'); + } + this.initSvg(); + this.render(false); + return undefined; + }); + }, +}; + +Blockly.JavaScript.balance = block => { + const balanceType = block.getFieldValue('BALANCE_TYPE'); + + const code = `Bot.getBalance('${balanceType}')`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./block_holder.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./block_holder.js new file mode 100644 index 0000000000..d4a7f4ffb9 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./block_holder.js @@ -0,0 +1,25 @@ +import { translate } from '../../../../../../../../common/i18n'; + +Blockly.Blocks.block_holder = { + init() { + this.jsonInit({ + message0: translate('Blocks inside are ignored %1 %2'), + args0 : [ + { + type: 'input_dummy', + }, + { + type : 'input_statement', + name : 'USELESS_STACK', + check: null, + }, + ], + colour : '#fef1cf', + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Put your blocks in here to prevent them from being removed'), + }); + }, +}; + +Blockly.JavaScript.block_holder = () => ''; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./index.js new file mode 100644 index 0000000000..7aee0d7b88 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./index.js @@ -0,0 +1,6 @@ +import './balance'; +import './total_profit'; +import './total_runs'; +import './notify'; +import './loader'; +import './block_holder'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./loader.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./loader.js new file mode 100644 index 0000000000..4d07e477fb --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./loader.js @@ -0,0 +1,68 @@ +import { deleteBlocksLoadedBy, loadRemote, recoverDeletedBlock } from '../../../../../utils'; +import { observer as globalObserver } from '../../../../../../../../common/utils/observer'; +import { translate } from '../../../../../../../../common/utils/tools'; + +Blockly.Blocks.loader = { + init() { + this.loadedByMe = []; + this.loadedVariables = []; + + this.jsonInit({ + message0: translate('Load block from: %1'), + args0 : [ + { + type: 'field_input', + name: 'URL', + text: 'http://www.example.com/block.xml', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Load blocks from URL'), + }); + }, + onchange(ev) { + if (!this.isInFlyout && ev.type === 'change' && ev.element === 'disabled' && ev.blockId === this.id) { + if (ev.newValue === true) { + deleteBlocksLoadedBy(this.id); + } else { + const loader = Blockly.mainWorkspace.getBlockById(ev.blockId); + if (loader && loader.loadedByMe) { + loader.loadedByMe.forEach(blockId => + recoverDeletedBlock(Blockly.mainWorkspace.getBlockById(blockId)) + ); + } + } + } + + if ( + !this.isInFlyout && + (ev.type === 'change' && ev.element === 'field') && + ev.blockId === this.id && + !this.disabled + ) { + const { recordUndo } = Blockly.Events; + + Blockly.Events.recordUndo = false; + deleteBlocksLoadedBy(this.id); + loadRemote(this).then( + () => { + Blockly.Events.recordUndo = recordUndo; + globalObserver.emit('ui.log.success', translate('Blocks are loaded successfully')); + }, + e => { + Blockly.Events.recordUndo = recordUndo; + throw e; + } + ); + } + }, +}; + +Blockly.JavaScript.loader = block => { + if (block.loadedVariables.length) { + return `var ${block.loadedVariables.map(v => Blockly.JavaScript.variableDB_.safeName_(v)).toString()}`; + } + return ''; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./notify.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./notify.js new file mode 100644 index 0000000000..93f8ae13c7 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./notify.js @@ -0,0 +1,42 @@ +import config from '../../../../../../../common/const'; +import { translate } from '../../../../../../../../common/i18n'; + +Blockly.Blocks.notify = { + init() { + this.jsonInit({ + message0: translate('Notify %1 with sound: %2 %3'), + args0 : [ + { + type : 'field_dropdown', + name : 'NOTIFICATION_TYPE', + options: config.lists.NOTIFICATION_TYPE, + }, + { + type : 'field_dropdown', + name : 'NOTIFICATION_SOUND', + options: config.lists.NOTIFICATION_SOUND, + }, + { + type : 'input_value', + name : 'MESSAGE', + check: null, + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + tooltip : translate('Creates a notification'), + }); + }, +}; + +Blockly.JavaScript.notify = block => { + const notificationType = block.getFieldValue('NOTIFICATION_TYPE'); + const sound = block.getFieldValue('NOTIFICATION_SOUND'); + const message = Blockly.JavaScript.valueToCode(block, 'MESSAGE') || ''; + + const code = `Bot.notify({ className: '${notificationType}', message: ${message}, sound: '${sound}'});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./total_profit.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./total_profit.js new file mode 100644 index 0000000000..46b5dd065d --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./total_profit.js @@ -0,0 +1,17 @@ +import { translate } from '../../../../../../../../common/i18n'; + +Blockly.Blocks.total_profit = { + init() { + this.jsonInit({ + message0 : translate('Total Profit'), + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the total profit'), + }); + }, +}; + +Blockly.JavaScript.total_profit = () => ['Bot.getTotalProfit()', Blockly.JavaScript.ORDER_ATOMIC]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./total_runs.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./total_runs.js new file mode 100644 index 0000000000..00fe14e1d9 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./total_runs.js @@ -0,0 +1,17 @@ +import { translate } from '../../../../../../../../common/i18n'; + +Blockly.Blocks.total_runs = { + init() { + this.jsonInit({ + message0 : translate('Number of Runs'), + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the number of runs since the beginning'), + }); + }, +}; + +Blockly.JavaScript.total_runs = () => ['Bot.getTotalRuns()', Blockly.JavaScript.ORDER_ATOMIC]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/epoch.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/epoch.js new file mode 100644 index 0000000000..1ebfaa3685 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/epoch.js @@ -0,0 +1,17 @@ +import { translate } from '../../../../../../../../common/i18n'; + +Blockly.Blocks.epoch = { + init() { + this.jsonInit({ + message0 : translate('Seconds Since Epoch'), + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + tooltip : translate('Returns the epoch time (seconds since epoch)'), + }); + }, +}; + +Blockly.JavaScript.epoch = () => ['Bot.getTime()', Blockly.JavaScript.ORDER_ATOMIC]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/index.js new file mode 100644 index 0000000000..ee9672a0bb --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/index.js @@ -0,0 +1,2 @@ +import './epoch'; +import './timeout'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/timeout.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/timeout.js new file mode 100644 index 0000000000..b579a7f9fb --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Time/timeout.js @@ -0,0 +1,58 @@ +import { translate } from '../../../../../../../../common/i18n'; + +Blockly.Blocks.timeout = { + init() { + this.jsonInit({ + message0: translate('%1 %2 Run after %3 second(s)'), + args0 : [ + { + type: 'input_dummy', + }, + { + type: 'input_statement', + name: 'TIMEOUTSTACK', + }, + { + type: 'input_value', + name: 'SECONDS', + }, + ], + colour : '#fef1cf', + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + tooltip : translate('Run the blocks inside every n seconds'), + }); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const allowedScopes = [ + 'trade_definition', + 'during_purchase', + 'before_purchase', + 'after_purchase', + 'tick_analysis', + ]; + if (allowedScopes.some(scope => this.isDescendantOf(scope))) { + if (this.disabled) { + this.setDisabled(false); + } + } else if (!this.disabled) { + this.setDisabled(true); + } + } + }, +}; + +Blockly.JavaScript.timeout = block => { + const stack = Blockly.JavaScript.statementToCode(block, 'TIMEOUTSTACK'); + const seconds = Blockly.JavaScript.valueToCode(block, 'SECONDS', Blockly.JavaScript.ORDER_ATOMIC) || '1'; + + const code = `sleep(${seconds});\n${stack}\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/index.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/index.js new file mode 100644 index 0000000000..247898e86a --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/index.js @@ -0,0 +1,8 @@ +import './trade_definition'; +import './trade_definition_market'; +import './trade_definition_tradetype'; +import './trade_definition_contracttype'; +import './trade_definition_candleinterval'; +import './trade_definition_restartbuysell'; +import './trade_definition_restartonerror'; +import './trade_definition_tradeoptions'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition.js new file mode 100644 index 0000000000..bdd64d1312 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition.js @@ -0,0 +1,176 @@ +import { translate } from '../../../../../../../common/i18n'; +import { setBlockTextColor, hideEventsFromBlockly } from '../../../../utils'; +import { defineContract } from '../../../images'; +import config from '../../../../../../common/const'; + +Blockly.Blocks.trade_definition = { + init() { + this.jsonInit({ + type : 'trade_definition', + message0: translate('%1 (1) Define your trade contract %2'), + message1: '%1', + message2: translate('Run Once at Start: %1'), + message3: '%1', + message4: translate('Define Trade Options: %1'), + message5: '%1', + args0 : [ + { + type : 'field_image', + src : defineContract, + width : 25, + height: 25, + alt : 'T', + }, + { + type: 'input_dummy', + }, + ], + args1: [ + { + type: 'input_statement', + name: 'TRADE_OPTIONS', + }, + ], + args2: [ + { + type: 'input_dummy', + }, + ], + args3: [ + { + type : 'input_statement', + name : 'INITIALIZATION', + check: null, + }, + ], + args4: [ + { + type: 'input_dummy', + }, + ], + args5: [ + { + type: 'input_statement', + name: 'SUBMARKET', + }, + ], + colour : '#2a3052', + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, + onchange(event) { + setBlockTextColor(this); + if (!this.workspace || this.isInFlyout) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + this.enforceTradeDefinitionType(); + } else if (event.type === Blockly.Events.BLOCK_CREATE) { + if (event.ids && event.ids.includes(this.id)) { + // Maintain single instance of this block + this.workspace.getAllBlocks(true).forEach(block => { + if (block.type === this.type && block.id !== this.id) { + block.dispose(); + } + }); + + const tradeDefinitionMarket = this.getChildByType('trade_definition_market'); + if (!tradeDefinitionMarket) { + return; + } + + const selectedMarket = tradeDefinitionMarket.getFieldValue('MARKET_LIST'); + const eventArgs = [tradeDefinitionMarket, 'field', 'MARKET_LIST', '', selectedMarket]; + const changeEvent = new Blockly.Events.BlockChange(...eventArgs); + Blockly.Events.fire(changeEvent); + } + } + }, + // Check if blocks within statement are valid, we enforce + // this statement to only allow `trade_definition` type blocks. + enforceTradeDefinitionType() { + const blocksInStatement = this.getBlocksInStatement('TRADE_OPTIONS'); + + blocksInStatement.forEach(block => { + if (!/^trade_definition_.+$/.test(block.type)) { + Blockly.Events.disable(); + block.unplug(true); + Blockly.Events.enable(); + } + }); + + const paramsToReadd = this.requiredParamBlocks.filter(blockName => blocksInStatement.findIndex(block => block.type === blockName) === -1); + + paramsToReadd.forEach(blockName => { + Blockly.Events.disable(); + + const block = this.workspace.newBlock(blockName); + block.initSvg(); + block.render(); + + const lastConnection = this.getLastConnectionInStatement('TRADE_OPTIONS'); + lastConnection.connect(block.previousConnection); + + Blockly.Events.enable(); + }); + + // Send CREATE event to re-populate dropdowns. + this.onchange({ type: Blockly.Events.BLOCK_CREATE, blockId: this.id }); + }, + requiredParamBlocks: [ + 'trade_definition_market', + 'trade_definition_tradetype', + 'trade_definition_contracttype', + 'trade_definition_candleinterval', + 'trade_definition_restartbuysell', + 'trade_definition_restartonerror', + ], +}; + +Blockly.JavaScript.trade_definition = block => { + const account = $('.account-id') + .first() + .attr('value'); + if (!account) { + throw Error('Please login'); + } + + const symbol = block.getChildFieldValue('trade_definition_market', 'SYMBOL_LIST') || ''; + const tradeType = block.getChildFieldValue('trade_definition_tradetype', 'TRADETYPE_LIST') || ''; + + // Contract Type (not referring the block) + const contractTypeBlock = block.getChildByType('trade_definition_contracttype'); + const contractTypeSelector = contractTypeBlock.getFieldValue('TYPE_LIST'); + const oppositesName = tradeType.toUpperCase(); + const contractTypeList = + contractTypeSelector === 'both' + ? config.opposites[oppositesName].map(k => Object.keys(k)[0]) + : [contractTypeSelector]; + + const candleIntervalValue = + block.getChildFieldValue('trade_definition_candleinterval', 'CANDLEINTERVAL_LIST') || 'default'; + const shouldRestartOnError = block.childValueToCode('trade_definition_restartonerror', 'RESTARTONERROR') || 'FALSE'; + const timeMachineEnabled = + block.childValueToCode('trade_definition_restartbuysell', 'TIME_MACHINE_ENABLED') || 'FALSE'; + + const initialization = Blockly.JavaScript.statementToCode(block, 'INITIALIZATION'); + const tradeOptionsStatement = Blockly.JavaScript.statementToCode(block, 'SUBMARKET'); + + const code = ` + BinaryBotPrivateInit = function BinaryBotPrivateInit() { + Bot.init('${account}', { + symbol: '${symbol}', + contractTypes: ${JSON.stringify(contractTypeList)}, + candleInterval: '${candleIntervalValue}', + shouldRestartOnError: ${shouldRestartOnError}, + timeMachineEnabled: ${timeMachineEnabled}, + }); + ${initialization.trim()} + }; + BinaryBotPrivateStart = function BinaryBotPrivateStart() { + ${tradeOptionsStatement.trim()} + };\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_candleinterval.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_candleinterval.js new file mode 100644 index 0000000000..c394d7401c --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_candleinterval.js @@ -0,0 +1,35 @@ +import config from '../../../../../../common/const'; + +Blockly.Blocks.trade_definition_candleinterval = { + init() { + this.jsonInit({ + message0: 'Default Candle Interval: %1', + args0 : [ + { + type : 'field_dropdown', + name : 'CANDLEINTERVAL_LIST', + options: config.candleIntervals.slice(1), + }, + ], + colour : Blockly.Colours.BinaryLessPurple.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + this.enforceParent(); + } + }, + enforceParent: Blockly.Blocks.trade_definition_market.enforceParent, +}; +Blockly.JavaScript.trade_definition_candleinterval = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_contracttype.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_contracttype.js new file mode 100644 index 0000000000..212bd14284 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_contracttype.js @@ -0,0 +1,70 @@ +import { translate } from '../../../../../../../common/utils/tools'; +import { oppositesToDropdown } from '../../../../utils'; +import config from '../../../../../../common/const'; + +Blockly.Blocks.trade_definition_contracttype = { + init() { + this.jsonInit({ + type : 'trade_definition', + message0: 'Contract Type: %1', + args0 : [ + { + type : 'field_dropdown', + name : 'TYPE_LIST', + options: [['', '']], + }, + ], + previousStatement: 'trade_definition', + nextStatement : 'trade_definition', + colour : Blockly.Colours.BinaryLessPurple.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + this.setMovable(false); + this.setDeletable(false); + }, + onchange(event) { + const allowedEvents = [Blockly.Events.BLOCK_CREATE, Blockly.Events.BLOCK_CHANGE, Blockly.Events.END_DRAG]; + if (!this.workspace || this.isInFlyout || !allowedEvents.includes(event.type) || this.workspace.isDragging()) { + return; + } + + const topParentBlock = this.getTopParent(); + if (!topParentBlock || topParentBlock.type !== 'trade_definition') { + this.enforceParent(); + return; + } + + const getContractTypes = () => { + const tradeTypeBlock = topParentBlock.getChildByType('trade_definition_tradetype'); + const tradeType = tradeTypeBlock && tradeTypeBlock.getFieldValue('TRADETYPE_LIST'); + if (tradeType) { + return [[translate('Both'), 'both'], ...oppositesToDropdown(config.opposites[tradeType.toUpperCase()])]; + } + return [['', '']]; + }; + + const updateTypeList = (useDefault = true) => { + const typeList = this.getField('TYPE_LIST'); + const typeListArgs = [getContractTypes()]; + if (useDefault) { + typeListArgs.push(typeList.getValue()); + } + typeList.updateOptions(...typeListArgs); + }; + + if (event.type === Blockly.Events.BLOCK_CHANGE) { + if (event.name === 'TRADETYPE_LIST') { + updateTypeList(); + } + } else if (event.type === Blockly.Events.BLOCK_CREATE) { + if (event.ids.includes(this.id)) { + updateTypeList(); + } + } + }, + enforceParent: Blockly.Blocks.trade_definition_market.enforceParent, +}; +Blockly.JavaScript.trade_definition_contracttype = () => ''; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_market.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_market.js new file mode 100644 index 0000000000..800f01fb7d --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_market.js @@ -0,0 +1,84 @@ +import { fieldGeneratorMapping } from '../../../shared'; + +Blockly.Blocks.trade_definition_market = { + init() { + this.jsonInit({ + type : 'trade_definition', + message0: 'Market: %1 Submarket: %2 Symbol: %3', + args0 : [ + { + type : 'field_dropdown', + name : 'MARKET_LIST', + options: fieldGeneratorMapping.MARKET_LIST, + }, + { + type : 'field_dropdown', + name : 'SUBMARKET_LIST', + options: [['', '']], + }, + { + type : 'field_dropdown', + name : 'SYMBOL_LIST', + options: [['', '']], + }, + ], + previousStatement: 'trade_definition', + nextStatement : 'trade_definition', + colour : Blockly.Colours.BinaryLessPurple.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange(event) { + const allowedEvents = [Blockly.Events.BLOCK_CREATE, Blockly.Events.BLOCK_CHANGE, Blockly.Events.END_DRAG]; + if (!this.workspace || this.isInFlyout || !allowedEvents.includes(event.type) || this.workspace.isDragging()) { + return; + } + + const topParentBlock = this.getTopParent(); + if (!topParentBlock || topParentBlock.type !== 'trade_definition') { + this.enforceParent(); + return; + } + + const updateMarketLists = (fields, useDefault = true) => { + fields.forEach(field => { + const list = this.getField(field); + const listArgs = [fieldGeneratorMapping[field](this)()]; + if (useDefault) { + listArgs.push(list.getValue()); + } + list.updateOptions(...listArgs); + }); + }; + + if (event.type === Blockly.Events.BLOCK_CREATE) { + if (event.ids.includes(this.id)) { + updateMarketLists(['SUBMARKET_LIST', 'SYMBOL_LIST']); + } + } else if (event.type === Blockly.Events.BLOCK_CHANGE) { + if (event.blockId === this.id) { + if (event.name === 'MARKET_LIST') { + updateMarketLists(['SUBMARKET_LIST']); + } else if (event.name === 'SUBMARKET_LIST') { + updateMarketLists(['SYMBOL_LIST']); + } + } + } + }, + enforceParent() { + if (!this.isDescendantOf('trade_definition')) { + Blockly.Events.disable(); + this.unplug(true); + this.dispose(); + Blockly.Events.enable(); + } + }, +}; + +Blockly.JavaScript.trade_definition_market = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartbuysell.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartbuysell.js new file mode 100644 index 0000000000..730bb82c8e --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartbuysell.js @@ -0,0 +1,37 @@ +Blockly.Blocks.trade_definition_restartbuysell = { + init() { + this.jsonInit({ + type : 'trade_definition', + message0: 'Restart buy/sell on error (disable for better performance): %1', + args0 : [ + { + type : 'input_value', + name : 'TIME_MACHINE_ENABLED', + check: 'Boolean', + }, + ], + previousStatement: 'trade_definition', + nextStatement : 'trade_definition', + colour : Blockly.Colours.BinaryLessPurple.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange(event) { + const allowedEvents = [Blockly.Events.BLOCK_CREATE, Blockly.Events.BLOCK_CHANGE, Blockly.Events.END_DRAG]; + if (!this.workspace || this.isInFlyout || !allowedEvents.includes(event.type) || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + this.enforceParent(); + } + }, + enforceParent: Blockly.Blocks.trade_definition_market.enforceParent, +}; +Blockly.JavaScript.trade_definition_restartbuysell = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartonerror.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartonerror.js new file mode 100644 index 0000000000..df4afdb394 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartonerror.js @@ -0,0 +1,37 @@ +Blockly.Blocks.trade_definition_restartonerror = { + init() { + this.jsonInit({ + type : 'trade_definition', + message0: 'Restart last trade on error (bot ignores the unsuccessful trade): %1', + args0 : [ + { + type : 'input_value', + name : 'RESTARTONERROR', + check: 'Boolean', + }, + ], + previousStatement: 'trade_definition', + nextStatement : 'trade_definition', + colour : Blockly.Colours.BinaryLessPurple.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + this.setMovable(false); + this.setDeletable(false); + }, + onchange(event) { + const allowedEvents = [Blockly.Events.BLOCK_CREATE, Blockly.Events.BLOCK_CHANGE, Blockly.Events.END_DRAG]; + if (!this.workspace || this.isInFlyout || !allowedEvents.includes(event.type) || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + this.enforceParent(); + } + }, + enforceParent: Blockly.Blocks.trade_definition_market.enforceParent, +}; +Blockly.JavaScript.trade_definition_restartonerror = () => {}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradeoptions.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradeoptions.js new file mode 100644 index 0000000000..a2de4e6df2 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradeoptions.js @@ -0,0 +1,277 @@ +import { translate } from '../../../../../../../common/i18n'; +import { getBarriersForContracts, pollForContracts, getDurationsForContracts } from '../../../shared'; +import { findTopParentBlock, hideEventsFromBlockly } from '../../../../utils'; +import config from '../../../../../../common/const'; + +Blockly.Blocks.trade_definition_tradeoptions = { + init() { + this.jsonInit({ + type : 'trade_definition', + message0: translate('Duration: %1 %2 Stake: %3 %4'), + args0 : [ + { + type : 'field_dropdown', + name : 'DURATIONTYPE_LIST', + options: [['', '']], + }, + { + type: 'input_value', + name: 'DURATION', + }, + { + type : 'field_dropdown', + name : 'CURRENCY_LIST', + options: config.lists.CURRENCY, + }, + { + type : 'input_value', + name : 'AMOUNT', + check: 'Number', + }, + ], + colour : Blockly.Colours.BinaryLessPurple.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + // Ensure one of this type per statement-stack + this.setNextStatement(false); + }, + onchange(event) { + const allowedEvents = [Blockly.Events.BLOCK_CREATE, Blockly.Events.BLOCK_CHANGE, Blockly.Events.END_DRAG]; + if (!this.workspace || this.isInFlyout || !allowedEvents.includes(event.type) || this.workspace.isDragging()) { + return; + } + + const topParentBlock = this.getTopParent(); + if (!topParentBlock || topParentBlock.type !== 'trade_definition') { + if (!this.disabled) { + this.setDisabled(true); + } + return; + } else if (this.disabled) { + this.setDisabled(false); + } + + const symbol = topParentBlock.getChildFieldValue('trade_definition_market', 'SYMBOL_LIST') || ''; + const tradeTypeBlock = topParentBlock.getChildByType('trade_definition_tradetype'); + const tradeType = tradeTypeBlock.getFieldValue('TRADETYPE_LIST') || ''; + const durationUnit = this.getFieldValue('DURATIONTYPE_LIST'); + + if (event.type === Blockly.Events.BLOCK_CREATE) { + if (event.ids.includes(this.id)) { + pollForContracts(symbol).then(contracts => { + const durations = getDurationsForContracts(contracts, tradeType); + this.updateDurationInput(durations, true); + }); + } + } else if (event.type === Blockly.Events.BLOCK_CHANGE) { + if (event.blockId === this.id) { + if (event.name === 'DURATIONTYPE_LIST') { + pollForContracts(symbol).then(contracts => { + this.updateBarrierInputs(contracts, tradeType, durationUnit); + }); + } else if (event.name === 'BARRIERTYPE_LIST' || event.name === 'SECONDBARRIERTYPE_LIST') { + this.applyBarrierHandlebars(event.name); + pollForContracts(symbol).then(contracts => { + this.updateBarrierInputs(contracts, tradeType, durationUnit); + }); + } + } + } else if (event.type === Blockly.Events.END_DRAG) { + if (event.blockId === this.id) { + pollForContracts(symbol).then(contracts => { + const durations = getDurationsForContracts(contracts, tradeType); + this.updateDurationInput(durations, true); + }); + } + } + }, + createPredictionInput(predictionRange) { + hideEventsFromBlockly(() => { + if (this.getInput('PREDICTION')) { + return; + } + + this.appendDummyInput('PREDICTION_LABEL').appendField(translate('Prediction:')); + + const predictionInput = this.appendValueInput('PREDICTION'); + + // We can't determine which contract a user buys, so sometimes the prediction range + // returned may not be valid, start at minimum index 1 (if possible) to bypass that + const index = Math.min(1, predictionRange.length - 1); + const shadowBlock = this.workspace.newBlock('math_number'); + + shadowBlock.setShadow(true); + shadowBlock.setFieldValue(predictionRange[index], 'NUM'); + shadowBlock.outputConnection.connect(predictionInput.connection); + shadowBlock.initSvg(); + shadowBlock.render(true); + }); + }, + createBarrierInput(barriers, startIndex = 0) { + const inputNames = ['BARRIER', 'SECONDBARRIER']; + const inputLabels = [translate('High barrier'), translate('Low barrier')]; + + for (let i = startIndex; i < barriers.values.length; i++) { + if (this.getInput(inputNames[i])) { + return; + } + + const label = barriers.values.length === 1 ? translate('Barrier') : inputLabels[i]; + const input = this.appendValueInput(inputNames[i]) + .appendField(translate(label), `${inputNames[i]}_LABEL`) + .appendField(new Blockly.FieldDropdown(config.barrierTypes), `${inputNames[i]}TYPE_LIST`); + + const shadowBlock = this.workspace.newBlock('math_number'); + shadowBlock.setShadow(true); + shadowBlock.setFieldValue(barriers.values[i], 'NUM'); + shadowBlock.outputConnection.connect(input.connection); + shadowBlock.initSvg(); + shadowBlock.render(true); + } + }, + updateDurationInput(durations, setMinDuration) { + const durationList = this.getField('DURATIONTYPE_LIST'); + durationList.updateOptions(durations.map(duration => [duration.label, duration.unit])); + + const durationInput = this.getInput('DURATION'); + if (durationInput.connection.isConnected()) { + const targetBlock = durationInput.connection.targetBlock(); + if (targetBlock.isShadow()) { + const minDuration = durations.find(duration => duration.unit === durationList.getValue()); + if (setMinDuration) { + targetBlock.setFieldValue(minDuration.minimum, 'NUM'); + } + } + } + }, + // Only updates a single `trade_definition_tradeoptions` block, i.e. `this` instance + updateBarrierInputs(contracts, tradeType, durationUnit) { + const selectedBarrierTypes = [ + this.getFieldValue('BARRIERTYPE_LIST') || config.barrierTypes[0][1], + this.getFieldValue('SECONDBARRIERTYPE_LIST') || config.barrierTypes[1][1], + ]; + + const barriers = getBarriersForContracts(contracts, tradeType, durationUnit, selectedBarrierTypes); + const inputNames = ['BARRIER', 'SECONDBARRIER']; + + for (let i = 0; i < barriers.values.length; i++) { + const input = this.getInput(inputNames[i]); + + if (input && input.connection.isConnected()) { + const targetBlock = input.connection.targetBlock(); + const barrierTypeList = this.getField(`${inputNames[i]}TYPE_LIST`); + const absoluteType = [[translate('Absolute'), 'absolute']]; + + if (durationUnit === 'd') { + barrierTypeList.updateOptions(absoluteType, 'absolute'); + } else if (barriers.allowBothTypes || barriers.allowAbsoluteType) { + barrierTypeList.updateOptions( + [...config.barrierTypes, ...absoluteType], + barrierTypeList.getValue() + ); + } else { + barrierTypeList.updateOptions(config.barrierTypes, barrierTypeList.getValue()); + } + + if (targetBlock.isShadow()) { + targetBlock.setFieldValue(barriers.values[i], 'NUM'); + } + } + } + }, + // Allow only one type of barrier (i.e. either both offset or absolute barrier type) + applyBarrierHandlebars(barrierInputName) { + const newValue = this.getFieldValue(barrierInputName); + const otherBarrierListName = + barrierInputName === 'BARRIERTYPE_LIST' ? 'SECONDBARRIERTYPE_LIST' : 'BARRIERTYPE_LIST'; + const otherBarrierList = this.getField(otherBarrierListName); + + if (otherBarrierList) { + const otherBarrierType = otherBarrierList.getValue(); + + if (config.barrierTypes.findIndex(type => type[1] === newValue) !== -1 && otherBarrierType === 'absolute') { + const otherValue = config.barrierTypes.find(type => type[1] !== newValue); + + otherBarrierList.setValue(otherValue[1]); + } else if (newValue === 'absolute' && otherBarrierType !== 'absolute') { + otherBarrierList.setValue('absolute'); + } + } + }, + // Rebuild block from XML + domToMutation(xmlElement) { + const hasFirstBarrier = xmlElement.getAttribute('has_first_barrier') === 'true'; + const hasSecondBarrier = xmlElement.getAttribute('has_second_barrier') === 'true'; + const hasPrediction = xmlElement.getAttribute('has_prediction') === 'true'; + + if (hasFirstBarrier && hasSecondBarrier) { + this.createBarrierInput({ values: [1, 2] }); + } else if (hasFirstBarrier) { + this.createBarrierInput({ values: [1] }); + } else if (hasPrediction) { + } + }, + // Export mutations to XML + mutationToDom() { + const container = document.createElement('mutation'); + container.setAttribute('has_first_barrier', !!this.getInput('BARRIER')); + container.setAttribute('has_second_barrier', !!this.getInput('SECONDBARRIER')); + container.setAttribute('has_prediction', !!this.getInput('PREDICTION')); + return container; + }, + enforceParent: Blockly.Blocks.trade_definition_market.enforceParent, +}; + +Blockly.JavaScript.trade_definition_tradeoptions = block => { + const durationValue = Blockly.JavaScript.valueToCode(block, 'DURATION') || '0'; + const durationType = block.getFieldValue('DURATIONTYPE_LIST') || '0'; + const currency = block.getFieldValue('CURRENCY_LIST'); + const amount = Blockly.JavaScript.valueToCode(block, 'AMOUNT') || '0'; + + let predictionValue = 'undefined'; + + if (block.getInput('PREDICTION')) { + predictionValue = Blockly.JavaScript.valueToCode(block, 'PREDICTION') || '-1'; + } + + const getBarrierValue = (barrierOffsetType, value) => { + // Variables should not be encapsulated in quotes + if (/^(\d+(\.\d+)?)$/.test(value)) { + return barrierOffsetType === 'absolute' ? `'${value}'` : `'${barrierOffsetType}${value}'`; + } + return barrierOffsetType === 'absolute' ? value : `'${barrierOffsetType}' + ${value}`; + }; + + let barrierOffsetValue = 'undefined'; + let secondBarrierOffsetValue = 'undefined'; + + if (block.getInput('BARRIER')) { + const barrierOffsetType = block.getFieldValue('BARRIERTYPE_LIST'); + const value = Blockly.JavaScript.valueToCode(block, 'BARRIER') || '0'; + barrierOffsetValue = getBarrierValue(barrierOffsetType, value); + } + + if (block.getInput('SECONDBARRIER')) { + const barrierOffsetType = block.getFieldValue('SECONDBARRIERTYPE_LIST'); + const value = Blockly.JavaScript.valueToCode(block, 'SECONDBARRIER') || '0'; + secondBarrierOffsetValue = getBarrierValue(barrierOffsetType, value); + } + + const code = ` + Bot.start({ + limitations: BinaryBotPrivateLimitations, + duration: ${durationValue}, + duration_unit: '${durationType}', + currency: '${currency}', + amount: ${amount}, + prediction: ${predictionValue}, + barrierOffset: ${barrierOffsetValue}, + secondBarrierOffset: ${secondBarrierOffsetValue}, + }); + `; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradetype.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradetype.js new file mode 100644 index 0000000000..f42f217acf --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradetype.js @@ -0,0 +1,221 @@ +import { + fieldGeneratorMapping, + pollForContracts, + getPredictionForContracts, + getBarriersForContracts, + getDurationsForContracts, +} from '../../../shared'; +import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/utils/tools'; + +Blockly.Blocks.trade_definition_tradetype = { + init() { + this.jsonInit({ + type : 'trade_definition', + message0: translate('Trade Category: %1 Trade Type: %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'TRADETYPECAT_LIST', + options: [['', '']], + }, + { + type : 'field_dropdown', + name : 'TRADETYPE_LIST', + options: [['', '']], + }, + ], + previousStatement: 'trade_definition', + nextStatement : 'trade_definition', + colour : Blockly.Colours.BinaryLessPurple.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + this.setMovable(false); + this.setDeletable(false); + }, + onchange(event) { + const allowedEvents = [Blockly.Events.BLOCK_CREATE, Blockly.Events.BLOCK_CHANGE, Blockly.Events.END_DRAG]; + if (!this.workspace || this.isInFlyout || !allowedEvents.includes(event.type) || this.workspace.isDragging()) { + return; + } + + const topParentBlock = this.getTopParent(); + if (!topParentBlock || topParentBlock.type !== 'trade_definition') { + this.enforceParent(); + return; + } + const marketBlock = topParentBlock.getChildByType('trade_definition_market'); + if (!marketBlock) { + return; + } + const symbol = marketBlock.getFieldValue('SYMBOL_LIST'); + if (!symbol) { + return; + } + + const updateTradeTypeCatList = (useDefault = false) => { + const tradeTypeCatList = this.getField('TRADETYPECAT_LIST'); + const tradeTypeCatArgs = [fieldGeneratorMapping.TRADETYPECAT_LIST(marketBlock)()]; + if (useDefault) { + tradeTypeCatArgs.push(tradeTypeCatList.getValue()); + } + tradeTypeCatList.updateOptions(...tradeTypeCatArgs); + }; + + const updateTradeTypeList = (useDefault = false) => { + const tradeTypeList = this.getField('TRADETYPE_LIST'); + const tradeTypeArgs = [fieldGeneratorMapping.TRADETYPE_LIST(this)()]; + if (useDefault) { + tradeTypeArgs.push(tradeTypeList.getValue()); + } + tradeTypeList.updateOptions(...tradeTypeArgs); + }; + + if (event.type === Blockly.Events.BLOCK_CHANGE) { + if (event.name === 'MARKET_LIST' || event.name === 'SUBMARKET_LIST' || event.name === 'SYMBOL_LIST') { + updateTradeTypeCatList(); + } else if (event.name === 'TRADETYPECAT_LIST') { + updateTradeTypeList(); + } else if (event.name === 'TRADETYPE_LIST') { + pollForContracts(symbol).then(contracts => { + this.updatePredictionInputs(contracts); + this.updateBarrierInputs(contracts); + this.updateDurationInputs(contracts); + }); + } + } else if (event.type === Blockly.Events.BLOCK_CREATE) { + if (event.ids.includes(this.id)) { + updateTradeTypeCatList(true); + updateTradeTypeList(true); + pollForContracts(symbol).then(contracts => { + this.updateDurationInputs(contracts); + }); + } + } + }, + updateBarrierInputs(contracts) { + const topParentBlock = this.getTopParent(); + this.workspace + .getAllBlocks() + .filter(block => block.type === 'trade_definition_tradeoptions') + .forEach(tradeOptionsBlock => { + const barrierOffsetNames = ['BARRIER', 'SECONDBARRIER']; + const barrierLabels = [translate('High barrier'), translate('Low barrier')]; + + const tradeType = topParentBlock.getChildFieldValue('trade_definition_tradetype', 'TRADETYPE_LIST'); + const durationUnit = tradeOptionsBlock.getFieldValue('DURATIONTYPE_LIST'); + + const firstBarrierType = + tradeOptionsBlock.getFieldValue('BARRIERTYPE_LIST') || config.barrierTypes[0][1]; + const secondBarrierType = + tradeOptionsBlock.getFieldValue('SECONDBARRIERTYPE_LIST') || config.barrierTypes[1][1]; + const selectedBarrierTypes = [firstBarrierType, secondBarrierType]; + const barriers = getBarriersForContracts(contracts, tradeType, durationUnit, selectedBarrierTypes); + + if (barriers.values.length === 0) { + tradeOptionsBlock.removeInput('BARRIER', true); + tradeOptionsBlock.removeInput('SECONDBARRIER', true); + } else { + Blockly.Events.disable(); + + const firstBarrierInput = tradeOptionsBlock.getInput('BARRIER'); + const secondBarrierInput = tradeOptionsBlock.getInput('SECONDBARRIER'); + + if (barriers.values.length > 0) { + if (!firstBarrierInput) { + tradeOptionsBlock.createBarrierInput(barriers); + } + + if (barriers.values.length === 1 && secondBarrierInput) { + tradeOptionsBlock.removeInput('SECONDBARRIER'); + } else if (barriers.values.length === 2 && !secondBarrierInput) { + tradeOptionsBlock.createBarrierInput(barriers, 1); + // Ensure barrier inputs are displayed together + tradeOptionsBlock.moveInputBefore('BARRIER', 'SECONDBARRIER'); + } + + barriers.values.forEach((barrierValue, index) => { + const typeList = tradeOptionsBlock.getField(`${barrierOffsetNames[index]}TYPE_LIST`); + const typeInput = tradeOptionsBlock.getInput(barrierOffsetNames[index]); + const absoluteType = [[translate('Absolute'), 'absolute']]; + + if (durationUnit === 'd') { + typeList.updateOptions(absoluteType); + } else if (barriers.allowBothTypes || barriers.allowAbsoluteType) { + typeList.updateOptions([...config.barrierTypes, ...absoluteType]); + } else { + typeList.updateOptions(config.barrierTypes); + } + + if (barriers.values.length === 1) { + typeInput.fieldRow[0].setText(`${translate('Barrier')}:`); + } else { + typeInput.fieldRow[0].setText(`${barrierLabels[index]}:`); + } + }); + + // Updates Shadow Block values + const barrierInputArgs = [ + contracts, + tradeType, + tradeOptionsBlock.getFieldValue('DURATIONTYPE_LIST'), + ]; + tradeOptionsBlock.updateBarrierInputs(...barrierInputArgs); + } + + tradeOptionsBlock.initSvg(); + tradeOptionsBlock.render(); + + Blockly.Events.enable(); + } + }); + }, + updatePredictionInputs(contracts) { + const topParentBlock = this.getTopParent(); + const tradeType = topParentBlock.getChildFieldValue('trade_definition_tradetype', 'TRADETYPE_LIST'); + const predictionRange = getPredictionForContracts(contracts, tradeType); + + this.workspace + .getAllBlocks() + .filter(block => block.type === 'trade_definition_tradeoptions') + .forEach(tradeOptionsBlock => { + if (predictionRange.length === 0) { + tradeOptionsBlock.removeInput('PREDICTION_LABEL'); + tradeOptionsBlock.removeInput('PREDICTION'); + } else { + const predictionInput = tradeOptionsBlock.getInput('PREDICTION'); + if (predictionInput) { + // TODO: Set new suggested value from API, i.e. first value in prediction range + } else { + tradeOptionsBlock.createPredictionInput(predictionRange); + } + } + tradeOptionsBlock.initSvg(); + tradeOptionsBlock.render(true); + }); + }, + updateDurationInputs(contracts) { + const topParentBlock = this.getTopParent(); + const tradeType = topParentBlock.getChildFieldValue('trade_definition_tradetype', 'TRADETYPE_LIST'); + const durations = getDurationsForContracts(contracts, tradeType); + + this.workspace + .getAllBlocks() + .filter(block => block.type === 'trade_definition_tradeoptions') + .forEach(tradeOptionsBlock => { + const durationUnits = durations.map(duration => [duration.label, duration.unit]); + const durationList = tradeOptionsBlock.getField('DURATIONTYPE_LIST'); + durationList.updateOptions(durationUnits); + + const minDuration = durations.find(duration => duration.unit === durationList.getValue()); + if (minDuration) { + tradeOptionsBlock.updateDurationInput(durations, minDuration.minimum); + } + }); + }, + enforceParent: Blockly.Blocks.trade_definition_market.enforceParent, +}; +Blockly.JavaScript.trade_definition_tradetype = () => ''; diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/controls_if.js b/src/botPage/view/blockly/blocks/Scratch/Logic/controls_if.js new file mode 100644 index 0000000000..a60f3725b8 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/controls_if.js @@ -0,0 +1,232 @@ +import { plusIconDark, minusIconDark } from '../../images'; +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.controls_if = { + init() { + this.elseIfCount_ = 0; + this.elseCount_ = 0; + + this.jsonInit({ + message0: translate('if %1 then'), + message1: '%1', + args0 : [ + { + type : 'input_value', + name : 'IF0', + check: 'Boolean', + }, + ], + args1: [ + { + type: 'input_statement', + name: 'DO0', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + category : Blockly.Categories.control, + previousStatement: null, + nextStatement : null, + }); + + const addInputIcon = this.getAddInputIcon_(); + this.appendDummyInput('MUTATOR').appendField(addInputIcon); + }, + /** + * Create XML to represent the number of else-if and else inputs. + * @return {Element} XML storage element. + * @this Blockly.Block + */ + mutationToDom() { + if (!this.elseIfCount_ && !this.elseCount_) { + return null; + } + + const container = document.createElement('mutation'); + + if (this.elseIfCount_) { + container.setAttribute('elseif', this.elseIfCount_); + } + + if (this.elseCount_) { + container.setAttribute('else', 1); + } + + return container; + }, + /** + * Parse XML to restore the else-if and else inputs. + * @param {!Element} xmlElement XML storage element. + * @this Blockly.Block + */ + domToMutation(xmlElement) { + this.elseIfCount_ = parseInt(xmlElement.getAttribute('elseif'), 10) || 0; + this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0; + + this.updateShape_(); + }, + updateShape_() { + // Delete everything. + if (this.getInput('ELSE')) { + this.removeInput('ELSE'); + } + + let i = 1; + while (this.getInput(`IF${i}`)) { + this.removeInput(`IF${i}`); + this.removeInput(`DO${i}`); + + i++; + } + + if (this.getInput('MUTATOR')) { + this.removeInput('MUTATOR'); + } + + // Rebuild block + for (let i = 1; i <= this.elseIfCount_; i++) { + this.appendDummyInput(`IF_LABEL${i}`).appendField(translate('else if')); + this.appendValueInput(`IF${i}`).setCheck('Boolean'); + this.appendDummyInput(`THEN_LABEL${i}`).appendField(translate('then')); + this.appendDummyInput(`DELETE_ICON${i}`).appendField(this.getRemoveInputIcon_(i, false)); + this.appendStatementInput(`DO${i}`); + } + + if (this.elseCount_) { + this.appendDummyInput('ELSE_LABEL').appendField(translate('else')); + this.appendDummyInput('DELETE_ELSE').appendField(this.getRemoveInputIcon_(this.elseIfCount_ + 1, true)); + this.appendStatementInput('ELSE'); + } + + this.appendDummyInput('MUTATOR').appendField(this.getAddInputIcon_()); + + this.initSvg(); + this.render(); + }, + getAddInputIcon_() { + const onAddClick = () => { + if (!this.workspace || this.isInFlyout) { + return; + } + + const newInputNum = this.elseIfCount_ + 1; + + if (this.elseCount_ === 0) { + // No `elseif`, just add an `else`-statement + this.appendDummyInput('ELSE_LABEL').appendField(translate('else')); + this.appendDummyInput('DELETE_ELSE').appendField(this.getRemoveInputIcon_(newInputNum, true)); + this.appendStatementInput('ELSE'); + + this.elseCount_++; + } else { + // We've already got `elseif` + `else`, keep adding more `elseif`'s + this.appendDummyInput(`IF_LABEL${newInputNum}`).appendField(translate('else if')); + this.appendValueInput(`IF${newInputNum}`).setCheck('Boolean'); + this.appendDummyInput(`THEN_LABEL${newInputNum}`).appendField(translate('then')); + this.appendDummyInput(`DELETE_ICON${newInputNum}`).appendField( + this.getRemoveInputIcon_(newInputNum, false) + ); + this.appendStatementInput(`DO${newInputNum}`); + + this.elseIfCount_++; + } + + // We already have an else, this input needs to be moved to the bottom where it belongs. + if (this.getInput('ELSE')) { + this.moveInputBefore('ELSE_LABEL', null); + this.moveInputBefore('DELETE_ELSE', null); + this.moveInputBefore('ELSE', null); + } + + // Move plus-icon to the bottom + this.moveInputBefore('MUTATOR', null); + + this.initSvg(); + this.render(); + }; + + const fieldImage = new Blockly.FieldImage(plusIconDark, 24, 24, '+', onAddClick); + return fieldImage; + }, + getRemoveInputIcon_(index, isElseStack) { + const onRemoveClick = () => { + if (!this.workspace || this.isInFlyout) { + return; + } + + if (isElseStack) { + this.removeInput('ELSE_LABEL'); + this.removeInput('DELETE_ELSE'); + this.removeInput('ELSE'); + this.elseCount_ = 0; + } else { + // Determine which label it is, has to be done inside this function. + const inputNames = ['IF_LABEL', 'IF', 'THEN_LABEL', 'DELETE_ICON', 'DO']; + + inputNames.forEach(inputName => { + this.removeInput(`${inputName}${index}`); + + // Re-number inputs w/ indexes larger than this one, e.g. when removing `IF5` becomes `IF4` + let i = 1; + let j = 0; + + // e.g. we've removed `IF5`, name of larger input `IF6` should become `IF5` + let largerInput = this.getInput(inputName + (index + i)); + + while (largerInput) { + const newIndex = index + j; + largerInput.name = inputName + newIndex; + + // Re-attach click handler with correct index. + if (inputName === 'DELETE_ICON') { + largerInput.fieldRow.forEach((field, fieldIndex) => { + field.dispose(); + largerInput.fieldRow.splice(fieldIndex, 1); + }); + + largerInput.appendField(this.getRemoveInputIcon_(newIndex, false)); + } + + i++; + j++; + + largerInput = this.getInput(inputName + (index + i)); + } + }); + + this.elseIfCount_--; + } + }; + + const fieldImage = new Blockly.FieldImage(minusIconDark, 24, 24, '-', onRemoveClick); + return fieldImage; + }, +}; + +Blockly.JavaScript.controls_if = block => { + // If/elseif/else condition. + let n = 0; + let code = ''; + + do { + const condition = Blockly.JavaScript.valueToCode(block, `IF${n}`, Blockly.JavaScript.ORDER_NONE) || 'false'; + + // i.e. (else)? if { // code } + const keyword = n > 0 ? 'else if' : 'if'; + code += ` + ${keyword} (${condition}) { + ${Blockly.JavaScript.statementToCode(block, `DO${n}`)} + }`; + n++; + } while (block.getInput(`IF${n}`)); + + if (block.getInput('ELSE')) { + code += ` + else { + ${Blockly.JavaScript.statementToCode(block, 'ELSE')} + }`; + } + + return `${code}\n`; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/index.js b/src/botPage/view/blockly/blocks/Scratch/Logic/index.js new file mode 100644 index 0000000000..41abc76e9f --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/index.js @@ -0,0 +1,7 @@ +import './logic_compare'; +import './controls_if'; +import './logic_boolean'; +import './logic_operation'; +import './logic_null'; +import './logic_ternary'; +import './logic_negate'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/logic_boolean.js b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_boolean.js new file mode 100644 index 0000000000..53cffb3971 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_boolean.js @@ -0,0 +1,27 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.logic_boolean = { + init() { + this.jsonInit({ + message0: '%1', + args0 : [ + { + type : 'field_dropdown', + name : 'BOOL', + options: [[translate('true'), 'TRUE'], [translate('false'), 'FALSE']], + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + category : Blockly.Categories.operators, + }); + }, +}; + +Blockly.JavaScript.logic_boolean = block => { + const code = block.getFieldValue('BOOL') === 'TRUE' ? 'true' : 'false'; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/logic_compare.js b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_compare.js new file mode 100644 index 0000000000..f6475b51a9 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_compare.js @@ -0,0 +1,56 @@ +Blockly.Blocks.logic_compare = { + init() { + this.jsonInit({ + message0: '%1 %2 %3', + args0 : [ + { + type: 'input_value', + name: 'A', + }, + { + type : 'field_dropdown', + name : 'OP', + options: [ + ['=', 'EQ'], + ['\u2260', 'NEQ'], + ['\u200F<', 'LT'], + ['\u200F\u2264', 'LTE'], + ['\u200F>', 'GT'], + ['\u200F\u2265', 'GTE'], + ], + }, + { + type: 'input_value', + name: 'B', + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.logic_compare = block => { + const operatorMapping = { + EQ : '==', + NEQ: '!=', + LT : '<', + LTE: '<=', + GT : '>', + GTE: '>=', + }; + + const operator = operatorMapping[block.getFieldValue('OP') || 'EQ']; + const order = ['==', '!='].includes(operator) + ? Blockly.JavaScript.ORDER_EQUALITY + : Blockly.JavaScript.ORDER_RELATIONAL; + + const argument0 = Blockly.JavaScript.valueToCode(block, 'A', order); + const argument1 = Blockly.JavaScript.valueToCode(block, 'B', order); + + const code = `${argument0} ${operator} ${argument1}`; + return [code, order]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/logic_negate.js b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_negate.js new file mode 100644 index 0000000000..0fc5b05cc1 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_negate.js @@ -0,0 +1,28 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.logic_negate = { + init() { + this.jsonInit({ + message0: translate('not %1'), + args0 : [ + { + type: 'input_value', + name: 'BOOL', + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.logic_negate = block => { + const order = Blockly.JavaScript.ORDER_LOGICAL_NOT; + const argument0 = Blockly.JavaScript.valueToCode(block, 'BOOL', order) || 'true'; + + const code = `!${argument0}`; + return [code, order]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/logic_null.js b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_null.js new file mode 100644 index 0000000000..1a7fa47ff9 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_null.js @@ -0,0 +1,13 @@ +Blockly.Blocks.logic_null = { + init() { + this.jsonInit({ + message0 : 'null', + output : null, + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; +Blockly.JavaScript.logic_null = () => ['null', Blockly.JavaScript.ORDER_ATOMIC]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/logic_operation.js b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_operation.js new file mode 100644 index 0000000000..f75ad38010 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_operation.js @@ -0,0 +1,49 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.logic_operation = { + init() { + this.jsonInit({ + message0: '%1 %2 %3', + args0 : [ + { + type: 'input_value', + name: 'A', + }, + { + type : 'field_dropdown', + name : 'OP', + options: [[translate('and'), 'AND'], [translate('or'), 'OR']], + }, + { + type: 'input_value', + name: 'B', + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.logic_operation = block => { + const selectedOperator = block.getFieldValue('OP'); + + let operator; + let order; + if (selectedOperator === 'AND') { + operator = '&&'; + order = Blockly.JavaScript.ORDER_LOGICAL_AND; + } else if (selectedOperator === 'OR') { + operator = '||'; + order = Blockly.JavaScript.ORDER_LOGICAL_OR; + } + + const argument0 = Blockly.JavaScript.valueToCode(block, 'A'); + const argument1 = Blockly.JavaScript.valueToCode(block, 'B'); + + const code = `${argument0} ${operator} ${argument1}`; + return [code, order]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/logic_ternary.js b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_ternary.js new file mode 100644 index 0000000000..7729f946b2 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/logic_ternary.js @@ -0,0 +1,44 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.logic_ternary = { + init() { + this.jsonInit({ + message0: translate('test %1'), + message1: translate('if true %1'), + message2: translate('if false %1'), + args0 : [ + { + type : 'input_value', + name : 'IF', + check: 'Boolean', + }, + ], + args1: [ + { + type: 'input_value', + name: 'THEN', + }, + ], + args2: [ + { + type: 'input_value', + name: 'ELSE', + }, + ], + output : null, + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.logic_ternary = block => { + const valueIf = Blockly.JavaScript.valueToCode(block, 'IF', Blockly.JavaScript.ORDER_CONDITIONAL) || 'false'; + const valueThen = Blockly.JavaScript.valueToCode(block, 'THEN', Blockly.JavaScript.ORDER_CONDITIONAL) || 'null'; + const valueElse = Blockly.JavaScript.valueToCode(block, 'ELSE', Blockly.JavaScript.ORDER_CONDITIONAL) || 'null'; + + const code = `${valueIf} ? ${valueThen} : ${valueElse}`; + return [code, Blockly.JavaScript.ORDER_CONDITIONAL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/index.js b/src/botPage/view/blockly/blocks/Scratch/Math/index.js new file mode 100644 index 0000000000..6c9d42f244 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/index.js @@ -0,0 +1,14 @@ +import './math_change'; +import './math_constant'; +import './math_constrain'; +import './math_modulo'; +import './math_number'; +import './math_number_property'; +import './math_on_list'; +import './math_random_float'; +import './math_random_int'; +import './math_round'; +import './math_arithmetic'; +import './math_single'; +import './math_trig'; + diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_arithmetic.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_arithmetic.js new file mode 100644 index 0000000000..ad4b0be66b --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_arithmetic.js @@ -0,0 +1,57 @@ +Blockly.Blocks.math_arithmetic = { + init() { + this.jsonInit({ + message0: '%1 %2 %3', + args0 : [ + { + type : 'input_value', + name : 'A', + check: 'Number', + }, + { + type : 'field_dropdown', + name : 'OP', + options: [['+', 'ADD'], ['-', 'MINUS'], ['*', 'MULTIPLY'], ['/', 'DIVIDE'], ['^', 'POWER']], + }, + { + type : 'input_value', + name : 'B', + check: 'Number', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_arithmetic = block => { + const operators = { + ADD : ['+', Blockly.JavaScript.ORDER_ADDITION], + MINUS : ['-', Blockly.JavaScript.ORDER_SUBTRACTION], + MULTIPLY: ['*', Blockly.JavaScript.ORDER_MULTIPLICATION], + DIVIDE : ['/', Blockly.JavaScript.ORDER_DIVISION], + POWER : [null, Blockly.JavaScript.ORDER_COMMA], // Handle power separately. + }; + + const tuple = operators[block.getFieldValue('OP')]; + const operator = tuple[0]; + const order = tuple[1]; + + const argument0 = Blockly.JavaScript.valueToCode(block, 'A', order) || '0'; + const argument1 = Blockly.JavaScript.valueToCode(block, 'B', order) || '0'; + + let code; + + // Power in JavaScript requires a special case since it has no operator. + if (!operator) { + code = `Math.pow(${argument0}, ${argument1})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } + + code = `${argument0} ${operator} ${argument1}`; + return [code, order]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_change.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_change.js new file mode 100644 index 0000000000..f5a3dfa2ea --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_change.js @@ -0,0 +1,39 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_change = { + init() { + this.jsonInit({ + message0: translate('change %1 by %2'), + args0 : [ + { + type : 'field_variable', + name : 'VAR', + variable: translate('item'), + }, + { + type : 'input_value', + name : 'DELTA', + check: 'Number', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, +}; + +Blockly.JavaScript.math_change = block => { + const variable = block.getFieldValue('VAR'); + const argument0 = Blockly.JavaScript.variableDB_.getName(variable, Blockly.Variables.NAME_TYPE); + const argument1 = Blockly.JavaScript.valueToCode(block, 'DELTA', Blockly.JavaScript.ORDER_ADDITION) || '0'; + + const code = ` + if (typeof ${argument0} != 'number') { + ${argument0} = 0; + }; + ${argument0} += ${argument1};\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_constant.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_constant.js new file mode 100644 index 0000000000..38432b11b1 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_constant.js @@ -0,0 +1,55 @@ +Blockly.Blocks.math_constant = { + init() { + this.jsonInit({ + message0: '%1', + args0 : [ + { + type : 'field_dropdown', + name : 'CONSTANT', + options: [ + ['\u03C0', 'PI'], + ['\u2107', 'E'], + ['\u03d5', 'GOLDEN_RATIO'], + ['sqrt(2)', 'SQRT2'], + ['sqrt(\u00bd)', 'SQRT1_2'], + ['\u221e', 'INFINITY'], + ], + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_constant = block => { + const constant = block.getFieldValue('CONSTANT'); + + let code; + let order; + + if (constant === 'PI') { + code = 'Math.PI'; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (constant === 'E') { + code = 'Math.E'; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (constant === 'GOLDEN_RATIO') { + code = '(1 + Math.sqrt(5)) / 2'; + order = Blockly.JavaScript.ORDER_DIVISION; + } else if (constant === 'SQRT2') { + code = 'Math.SQRT2'; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (constant === 'SQRT1_2') { + code = 'Math.SQRT1_2'; + order = Blockly.JavaScript.ORDER_MEMBER; + } else if (constant === 'INFINITY') { + code = 'Infinity'; + order = Blockly.JavaScript.ORDER_ATOMIC; + } + + return [code, order]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_constrain.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_constrain.js new file mode 100644 index 0000000000..a5096be93e --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_constrain.js @@ -0,0 +1,40 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_constrain = { + init() { + this.jsonInit({ + message0: translate('constrain %1 low %2 high %3'), + args0 : [ + { + type : 'input_value', + name : 'VALUE', + check: 'Number', + }, + { + type : 'input_value', + name : 'LOW', + check: 'Number', + }, + { + type : 'input_value', + name : 'HIGH', + check: 'Number', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_constrain = block => { + const argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_COMMA) || '0'; + const argument1 = Blockly.JavaScript.valueToCode(block, 'LOW', Blockly.JavaScript.ORDER_COMMA) || '0'; + const argument2 = Blockly.JavaScript.valueToCode(block, 'HIGH', Blockly.JavaScript.ORDER_COMMA) || '0'; + + const code = `Math.min(Math.max(${argument0}, ${argument1}), ${argument2})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_modulo.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_modulo.js new file mode 100644 index 0000000000..075efe651c --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_modulo.js @@ -0,0 +1,34 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_modulo = { + init() { + this.jsonInit({ + message0: translate('remainder of %1 ÷ %2'), + args0 : [ + { + type : 'input_value', + name : 'DIVIDEND', + check: 'Number', + }, + { + type : 'input_value', + name : 'DIVISOR', + check: 'Number', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_modulo = block => { + const argument0 = Blockly.JavaScript.valueToCode(block, 'DIVIDEND', Blockly.JavaScript.ORDER_MODULUS) || '0'; + const argument1 = Blockly.JavaScript.valueToCode(block, 'DIVISOR', Blockly.JavaScript.ORDER_MODULUS) || '0'; + + const code = `${argument0} % ${argument1}`; + return [code, Blockly.JavaScript.ORDER_MODULUS]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_number.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_number.js new file mode 100644 index 0000000000..1becb21f62 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_number.js @@ -0,0 +1,24 @@ +Blockly.Blocks.math_number = { + init() { + this.jsonInit({ + message0: '%1', + args0 : [ + { + type : 'field_number', + name : 'NUM', + value: 0, + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : '#dedede', + colourSecondary: '#ffffff', + colourTertiary : '#ffffff', + }); + }, +}; + +Blockly.JavaScript.math_number = block => { + const code = block.getFieldValue('NUM'); + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_number_property.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_number_property.js new file mode 100644 index 0000000000..b8ad110055 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_number_property.js @@ -0,0 +1,112 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_number_property = { + init() { + this.jsonInit({ + message0: translate('%1 is %2'), + args0 : [ + { + type: 'input_value', + name: 'NUMBER_TO_CHECK', + }, + { + type : 'field_dropdown', + name : 'PROPERTY', + options: [ + [translate('even'), 'EVEN'], + [translate('odd'), 'ODD'], + [translate('prime'), 'PRIME'], + [translate('whole'), 'WHOLE '], + [translate('positive'), 'POSITIVE'], + [translate('negative'), 'NEGATIVE'], + [translate('divisible by'), 'DIVISIBLE_BY'], + ], + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + + this.setOnChange(event => { + if (event.name === 'PROPERTY') { + const hasDivisorInput = this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY'; + this.updateShape(hasDivisorInput); + } + }); + }, + domToMutation(xmlElement) { + const hasDivisorInput = xmlElement.getAttribute('divisor_input') === 'true'; + this.updateShape(hasDivisorInput); + }, + mutationToDom() { + const container = document.createElement('mutation'); + const divisorInput = this.getFieldValue('PROPERTY') === 'DIVISIBLE_BY'; + container.setAttribute('divisor_input', divisorInput); + return container; + }, + updateShape(hasDivisorInput) { + const inputExists = this.getInput('DIVISOR'); + + if (hasDivisorInput) { + if (!inputExists) { + this.appendValueInput('DIVISOR').setCheck('Number'); + this.initSvg(); + this.render(false); + } + } else { + this.removeInput('DIVISOR'); + } + }, +}; + +Blockly.JavaScript.math_number_property = block => { + const argument0 = Blockly.JavaScript.valueToCode(block, 'NUMBER_TO_CHECK', Blockly.JavaScript.ORDER_MODULUS) || '0'; + const property = block.getFieldValue('PROPERTY'); + + let code; + + if (property === 'PRIME') { + const functionName = Blockly.JavaScript.provideFunction_('mathIsPrime', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}_(n) { + // https://en.wikipedia.org/wiki/Primality_test#Naive_methods + if (n == 2 || n == 3) { + return true; + } + + // False if n is NaN, negative, is 1, or not whole. + // And false if n is divisible by 2 or 3. + if (isNaN(n)) || n <= 1 || n % 1 != 0 || n % 2 == 0 || n % 3 == 0) { + return false; + } + + // Check all the numbers of form 6k +/- 1, up to sqrt(n). + for (let i = 6; x <= Math.sqrt(n) + 1; x += 6) { + if (n % (x - 1) == 0 || n % (x + 1) == 0 { + return false; + }) + } + return true; + }`, + ]); + code = `${functionName}(${argument0})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } else if (property === 'EVEN') { + code = `${argument0} % 2 === 0`; + } else if (property === 'ODD') { + code = `${argument0} % 2 === 1`; + } else if (property === 'WHOLE') { + code = `${argument0} % 1 === 0`; + } else if (property === 'POSITIVE') { + code = `${argument0} > 0`; + } else if (property === 'NEGATIVE') { + code = `${argument0} < 0`; + } else if (property === 'DIVISIBLE_BY') { + const divisor = Blockly.JavaScript.valueToCode(block, 'DIVISOR', Blockly.JavaScript.ORDER_MODULUS) || '0'; + code = `${argument0} % ${divisor} == 0`; + } + + return [code, Blockly.JavaScript.ORDER_EQUALITY]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_on_list.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_on_list.js new file mode 100644 index 0000000000..10de9f13ac --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_on_list.js @@ -0,0 +1,159 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_on_list = { + init() { + this.jsonInit({ + message0: translate('%1 of list %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'OPERATION', + options: [ + [translate('sum'), 'SUM'], + [translate('min'), 'MIN'], + [translate('max'), 'MAX'], + [translate('average'), 'AVERAGE'], + [translate('median'), 'MEDIAN'], + [translate('modes'), 'MODE'], + [translate('standard deviation'), 'STD_DEV'], + [translate('random item'), 'RANDOM'], + ], + }, + { + type: 'input_value', + name: 'LIST', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_on_list = block => { + const operation = block.getFieldValue('OPERATION'); + + let code; + let list; + + if (operation === 'SUM') { + list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_MEMBER) || '[]'; + code = `${list}.reduce(function(x, y) { return x + y; })`; + } else if (operation === 'MIN') { + list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_COMMA) || '[]'; + code = `Math.min.apply(null, ${list})`; + } else if (operation === 'MAX') { + list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_COMMA) || '[]'; + code = `Math.max.apply(null, ${list})`; + } else if (operation === 'AVERAGE') { + const functionName = Blockly.JavaScript.provideFunction_('mathMean', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) { + return myList.reduce(function(x, y) { + return x + y; + }) / myList.length; + }`, + ]); + + list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_NONE) || '[]'; + code = `${functionName}(${list})`; + } else if (operation === 'MEDIAN') { + const functionName = Blockly.JavaScript.provideFunction_('mathMedian', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(myList) { + const localList = myList.filter(function(x) { + return typeof x == 'number'; + }); + if (!localList.length) { + return null; + } + localList.sort(function(a, b) { + return b - a; + }); + if (localList.length % 2 == 0) { + return (localList[localList.length / 2 - 1] + localList[localList.length / 2]) / 2; + } else { + return localList[(localList.length - 1) / 2]; + } + }`, + ]); + + list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_NONE) || '[]'; + code = `${functionName}(${list})`; + } else if (operation === 'MODE') { + const functionName = Blockly.JavaScript.provideFunction_('mathModes', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(values) { + const modes = []; + const counts = []; + let maxCount = 0; + + for (let i = 0; i < values.length; i++) { + const value = values[i]; + let found = false; + let thisCount; + + for (let j = 0; j < counts.length; j++) { + if (counts[j][0] === value) { + thisCount = ++counts[j][1]; + found = true; + break; + } + } + + if (!found) { + counts.push([value, 1]); + thisCount = 1; + } + maxCount = Math.max(thisCount, maxCount); + } + + for (let j = 0; j < counts.length; j++) { + if (counts[j][1] == maxCount) { + modes.push(counts[j][0]); + } + } + + return modes; + }`, + ]); + + list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_NONE) || '[]'; + code = `${functionName}(${list})`; + } else if (operation === 'STD_DEV') { + const functionName = Blockly.JavaScript.provideFunction_('mathStandardDeviation', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(numbers) { + const n = numbers.length; + if (!n) { + return null; + } + + const mean = numbers.reduce(function(x, y) { + return x + y; + }) / n; + + let variance = 0; + for (let j = 0; j < n; j++) { + variance += Math.pow(numbers[j] - mean, 2); + } + variance = variance / n; + return Math.sqrt(variance); + }`, + ]); + + list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_NONE) || '[]'; + code = `${functionName}(${list})`; + } else if (operation === 'RANDOM') { + const functionName = Blockly.JavaScript.provideFunction_('mathRandomList', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list) { + const x = Math.floor(Math.random() * list.length); + return list[x]; + }`, + ]); + + list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_NONE) || '[]'; + code = `${functionName}(${list})`; + } + + return [code, Blockly.JavaScript.FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_random_float.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_random_float.js new file mode 100644 index 0000000000..ac14bb7849 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_random_float.js @@ -0,0 +1,16 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_random_float = { + init() { + this.jsonInit({ + message0 : translate('random fraction'), + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_random_float = () => ['Math.random()', Blockly.JavaScript.ORDER_FUNCTION_CALL]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_random_int.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_random_int.js new file mode 100644 index 0000000000..e8f9fa98e2 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_random_int.js @@ -0,0 +1,46 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_random_int = { + init() { + this.jsonInit({ + message0: translate('random integer from %1 to %2'), + args0 : [ + { + type : 'input_value', + name : 'FROM', + check: 'Number', + }, + { + type : 'input_value', + name : 'TO', + check: 'Number', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_random_int = block => { + const argument0 = Blockly.JavaScript.valueToCode(block, 'FROM', Blockly.JavaScript.ORDER_COMMA) || '0'; + const argument1 = Blockly.JavaScript.valueToCode(block, 'TO', Blockly.JavaScript.ORDER_COMMA) || '0'; + + const functionName = Blockly.JavaScript.provideFunction_('mathRandomInt', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(a, b) { + if (a > b) { + // Swap a and b to ensure a is smaller. + const c = a; + a = b; + b = c; + } + return Math.floor(Math.random() * (b - a + 1) + a); + }`, + ]); + + const code = `${functionName}(${argument0}, ${argument1})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_round.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_round.js new file mode 100644 index 0000000000..4df760b1cb --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_round.js @@ -0,0 +1,46 @@ +// https://github.com/google/blockly/blob/master/generators/javascript/math.js +Blockly.Blocks.math_round = { + /** + * Check if a number is even, odd, prime, whole, positive, or negative + * or if it is divisible by certain number. Returns true or false. + * @this Blockly.Block + */ + init() { + this.jsonInit({ + message0: '%1 %2', + args0 : [ + { + type : 'field_dropdown', + name : 'OP', + options: [['round', 'ROUND'], ['round up', 'ROUNDUP'], ['round down', 'ROUNDDOWN']], + }, + { + type: 'input_value', + name: 'NUM', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_round = block => { + const operation = block.getFieldValue('OP'); + const argument0 = Blockly.JavaScript.valueToCode(block, 'NUM') || '0'; + + let code; + + if (operation === 'ROUND') { + code = `Math.round(${argument0})`; + } else if (operation === 'ROUNDUP') { + code = `Math.ceil(${argument0})`; + } else if (operation === 'ROUNDDOWN') { + code = `Math.floor(${argument0})`; + } + + return [code, Blockly.JavaScript.FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_single.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_single.js new file mode 100644 index 0000000000..b1f968127c --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_single.js @@ -0,0 +1,101 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_single = { + init() { + this.jsonInit({ + message0: translate('%1 %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'OP', + options: [ + [translate('square root'), 'ROOT'], + [translate('absolute'), 'ABS'], + ['-', 'NEG'], + ['ln', 'LN'], + ['log10', 'LOG10'], + ['e^', 'EXP'], + ['10^', 'POW10'], + ], + }, + { + type: 'input_value', + name: 'NUM', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_single = block => { + const operator = block.getFieldValue('OP'); + + let code; + let arg; + + if (operator === 'NEG') { + // Negation is a special case given its different operator precedence. + arg = Blockly.JavaScript.valueToCode(block, 'NUM', Blockly.JavaScript.ORDER_UNARY_NEGATION) || '0'; + if (arg[0] === '-') { + // --3 is not legal in JS + arg = ` ${arg}`; + } + code = `-${arg}`; + return [code, Blockly.JavaScript.ORDER_UNARY_NEGATION]; + } + + if (['SIN', 'COS', 'TAN'].includes(operator)) { + arg = Blockly.JavaScript.valueToCode(block, 'NUM', Blockly.JavaScript.ORDER_DIVISION) || '0'; + } else { + arg = Blockly.JavaScript.valueToCode(block, 'NUM', Blockly.JavaScript.ORDER_NONE) || '0'; + } + + // First, handle cases which generate values that don't need parentheses + // wrapping the code. + if (operator === 'ABS') { + code = `Math.abs(${arg})`; + } else if (operator === 'ROOT') { + code = `Math.sqrt(${arg})`; + } else if (operator === 'LN') { + code = `Math.log(${arg})`; + } else if (operator === 'EXP') { + code = `Math.pow(Math.E, ${arg})`; + } else if (operator === 'POW10') { + code = `Math.pow(10, ${arg})`; + } else if (operator === 'ROUND') { + code = `Math.round(${arg})`; + } else if (operator === 'ROUNDUP') { + code = `Math.ceil(${arg})`; + } else if (operator === 'ROUNDDOWN') { + code = `Math.floor(${arg})`; + } else if (operator === 'SIN') { + code = `Math.sin(${arg} / 180 * Math.PI)`; + } else if (operator === 'COS') { + code = `Math.cos(${arg} / 180 * Math.PI)`; + } else if (operator === 'TAN') { + code = `Math.tan(${arg} / 180 * Math.PI)`; + } + + if (code) { + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; + } + + // Second, handle cases which generate values that may need parentheses + // wrapping the code. + if (operator === 'LOG10') { + code = `Math.log(${arg}) / Math.log(10)`; + } else if (operator === 'ASIN') { + code = `Math.asin(${arg} / 180 * Math.PI)`; + } else if (operator === 'ACOS') { + code = `Math.acos(${arg} / 180 * Math.PI)`; + } else if (operator === 'ATAN') { + code = `Math.atan(${arg} / 180 * Math.PI)`; + } + + return [code, Blockly.JavaScript.ORDER_DIVISION]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_trig.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_trig.js new file mode 100644 index 0000000000..bc0ab1ae7a --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_trig.js @@ -0,0 +1,35 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.math_trig = { + init() { + this.jsonInit({ + message0: translate('%1 %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'OP', + options: [ + ['sin', 'SIN'], + ['cos', 'COS'], + ['tan', 'TAN'], + ['asin', 'ASIN'], + ['acos', 'ACOS'], + ['atan', 'ATAN'], + ], + }, + { + type : 'input_value', + name : 'NUM', + check: 'Number', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.math_trig = Blockly.JavaScript.math_single; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/index.js b/src/botPage/view/blockly/blocks/Scratch/Text/index.js new file mode 100644 index 0000000000..bbdfbfdb23 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/index.js @@ -0,0 +1,13 @@ +import './text'; +import './text_join'; +import './text_statement'; +import './text_append'; +import './text_length'; +import './text_isEmpty'; +import './text_indexOf'; +import './text_charAt'; +import './text_getSubstring'; +import './text_changeCase'; +import './text_trim'; +import './text_print'; +import './text_prompt_ext'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text.js b/src/botPage/view/blockly/blocks/Scratch/Text/text.js new file mode 100644 index 0000000000..3425cb0638 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text.js @@ -0,0 +1,23 @@ +Blockly.Blocks.text = { + init() { + this.jsonInit({ + message0: '%1', + args0 : [ + { + type: 'field_input', + name: 'TEXT', + }, + ], + output : 'String', + outputShape : Blockly.OUTPUT_SHAPE_SQUARE, + colour : '#dedede', + colourSecondary: '#ffffff', + colourTertiary : '#ffffff', + }); + }, +}; + +Blockly.JavaScript.text = block => { + const code = Blockly.JavaScript.quote_(block.getFieldValue('TEXT')); + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_append.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_append.js new file mode 100644 index 0000000000..9c26bcf0da --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_append.js @@ -0,0 +1,52 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_append = { + init() { + this.jsonInit({ + message0: translate('to %1 append text %2'), + args0 : [ + { + type : 'field_variable', + name : 'VAR', + variable: translate('text'), + }, + { + type: 'input_value', + name: 'TEXT', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, +}; + +/** + * Enclose the provided value in 'String(...)' function. + * Leave string literals alone. + * @param {string} value Code evaluating to a value. + * @return {string} Code evaluating to a string. + * @private + */ +Blockly.JavaScript.text.forceString_ = function(value) { + if (Blockly.JavaScript.text.forceString_.strRegExp.test(value)) { + return value; + } + return `String(${value})`; +}; + +/** + * Regular expression to detect a single-quoted string literal. + */ +Blockly.JavaScript.text.forceString_.strRegExp = /^\s*'([^']|\\')*'\s*$/; + +Blockly.JavaScript.text_append = block => { + const varName = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); + const value = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_NONE) || '\'\''; + + const code = `${varName} += ${Blockly.JavaScript.text.forceString_(value)};\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_changeCase.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_changeCase.js new file mode 100644 index 0000000000..c7efaf3687 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_changeCase.js @@ -0,0 +1,57 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_changeCase = { + init() { + this.jsonInit({ + message0: translate('to %1 %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'CASE', + options: [ + [translate('UPPER CASE'), 'UPPERCASE'], + [translate('lower case'), 'LOWERCASE'], + [translate('Title Case'), 'TITLECASE'], + ], + }, + { + type: 'input_value', + name: 'TEXT', + }, + ], + output : 'String', + outputShape : Blockly.OUTPUT_SHAPE_SQUARE, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.text_changeCase = block => { + const operators = { + UPPERCASE: '.toUpperCase()', + LOWERCASE: '.toLowerCase()', + TITLECASE: null, + }; + const operator = operators[block.getFieldValue('CASE')]; + const textOrder = operator ? Blockly.JavaScript.ORDER_MEMBER : Blockly.JavaScript.ORDER_NONE; + const text = Blockly.JavaScript.valueToCode(block, 'TEXT', textOrder) || '\'\''; + + let code; + + if (operator) { + code = `${text}${operator}`; + } else { + const functionName = Blockly.JavaScript.provideFunction_('textToTitleCase', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(str) { + return str.toLowerCase().split(' ').map(function(word) { + return word.replace(word[0], word[0].toUpperCase()); + }).join(' '); + }`, + ]); + code = `${functionName}(${text})`; + } + + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_charAt.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_charAt.js new file mode 100644 index 0000000000..1d9464692f --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_charAt.js @@ -0,0 +1,94 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_charAt = { + init() { + this.jsonInit({ + message0: translate('in text %1 get %2'), + args0 : [ + { + type: 'input_value', + name: 'VALUE', + }, + { + type : 'field_dropdown', + name : 'WHERE', + options: [ + ['#', 'FROM_START'], + [translate('# from end'), 'FROM_END'], + [translate('first letter'), 'FIRST'], + [translate('last letter'), 'LAST'], + [translate('random letter'), 'RANDOM'], + ], + }, + ], + output : 'String', + outputShape : Blockly.OUTPUT_SHAPE_SQUARE, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + + const dropdown = this.getField('WHERE'); + dropdown.setValidator(value => { + const newAt = ['FROM_START', 'FROM_END'].includes(value); + if (newAt !== this.isAt_) { + this.updateAt_(newAt); + this.setFieldValue(value, 'WHERE'); + return null; + } + return undefined; + }); + + this.updateAt_(true); + }, + mutationToDom() { + const container = document.createElement('mutation'); + container.setAttribute('at', !!this.isAt_); + return container; + }, + domToMutation(xmlElement) { + const isAt = xmlElement.getAttribute('at') != 'false'; + this.updateAt_(isAt); + }, + updateAt_(isAt) { + this.removeInput('AT', true); + if (isAt) { + this.appendValueInput('AT').setCheck('Number'); + } + + this.isAt_ = isAt; + this.initSvg(); + this.render(false); + }, +}; + +Blockly.JavaScript.text_charAt = block => { + const where = block.getFieldValue('WHERE') || 'FROM_START'; + const textOrder = where === 'RANDOM' ? Blockly.JavaScript.ORDER_NONE : Blockly.JavaScript.ORDER_MEMBER; + const text = Blockly.JavaScript.valueToCode(block, 'VALUE', textOrder) || '\'\''; + + let code; + + if (where === 'FROM_START') { + const at = Blockly.JavaScript.getAdjusted(block, 'AT'); + // Adjust index if using one-based indices + code = `${text}.charAt(${at})`; + } else if (where === 'FROM_END') { + const at = Blockly.JavaScript.getAdjusted(block, 'AT', 1, true); + code = `${text}.slice(${at}).charAt(0)`; + } else if (where === 'FIRST') { + code = `${text}.charAt(0)`; + } else if (where === 'LAST') { + code = `${text}.slice(-1)`; + } else if (where === 'RANDOM') { + const functionName = Blockly.JavaScript.provideFunction_('textRandomLetter', [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(text) { + var x = Math.floor(Math.random() * text.length); + return text[x]; + }`, + ]); + code = `${functionName}(${text})`; + } + + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_getSubstring.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_getSubstring.js new file mode 100644 index 0000000000..dcd887f303 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_getSubstring.js @@ -0,0 +1,163 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_getSubstring = { + init() { + this.WHERE_OPTIONS_1 = [ + [translate('letter\u00A0#'), 'FROM_START'], + [translate('letter\u00A0#\u00A0from end'), 'FROM_END'], + [translate('first'), 'FIRST'], + ]; + this.WHERE_OPTIONS_2 = [ + [translate('letter\u00A0#'), 'FROM_START'], + [translate('letter\u00A0#\u00A0from end'), 'FROM_END'], + [translate('last'), 'LAST'], + ]; + + this.jsonInit({ + message0: translate('in text %1 get substring from %2 %3 to %4 %5'), + args0 : [ + { + type: 'input_value', + name: 'STRING', + }, + { + type : 'field_dropdown', + name : 'WHERE1', + options: this.WHERE_OPTIONS_1, + }, + { + type: 'input_dummy', + name: 'AT1', + }, + { + type : 'field_dropdown', + name : 'WHERE2', + options: this.WHERE_OPTIONS_2, + }, + { + type: 'input_dummy', + name: 'AT2', + }, + ], + output : 'String', + outputShape : Blockly.OUTPUT_SHAPE_SQUARE, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + + this.updateAt_(1, true); + this.updateAt_(2, true); + }, + mutationToDom() { + const container = document.createElement('mutation'); + const isAt1 = this.getInput('AT1').type === Blockly.INPUT_VALUE; + const isAt2 = this.getInput('AT2').type === Blockly.INPUT_VALUE; + + container.setAttribute('at1', isAt1); + container.setAttribute('at2', isAt2); + + return container; + }, + domToMutation(xmlElement) { + const isAt1 = xmlElement.getAttribute('at1') === 'true'; + const isAt2 = xmlElement.getAttribute('at2') === 'true'; + + this.updateAt_(1, isAt1); + this.updateAt_(2, isAt2); + }, + updateAt_(n, isAt) { + this.removeInput(`AT${n}`, true); + if (isAt) { + this.appendValueInput(`AT${n}`).setCheck('Number'); + } else { + this.appendDummyInput(`AT${n}`); + } + + const menu = new Blockly.FieldDropdown(this[`WHERE_OPTIONS_${n}`], value => { + const newAt = ['FROM_START', 'FROM_END'].includes(value); + if (newAt !== isAt) { + this.updateAt_(n, newAt); + this.setFieldValue(value, `WHERE${n}`); + return null; + } + return undefined; + }); + + this.getInput(`AT${n}`).appendField(menu, `WHERE${n}`); + if (n === 1) { + this.moveInputBefore('AT1', 'AT2'); + } + + this.initSvg(); + this.render(false); + }, +}; + +Blockly.JavaScript.text_getSubstring = block => { + const text = Blockly.JavaScript.valueToCode(block, 'STRING', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\''; + const where1 = block.getFieldValue('WHERE1'); + const where2 = block.getFieldValue('WHERE2'); + + let at1; + let at2; + let code; + + if (where1 === 'FIRST' && where2 === 'LAST') { + code = text; + } else if ( + text.match(/^'?\w+'?$/) || + (where1 !== 'FROM_END' && where1 !== 'LAST' && where2 !== 'FROM_END' && where2 !== 'LAST') + ) { + if (where1 === 'FROM_START') { + at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + } else if (where1 === 'FROM_END') { + at1 = Blockly.JavaScript.getAdjusted(block, 'AT1', 1, false, Blockly.JavaScript.ORDER_SUBTRACTION); + at1 = `${text}.length - ${at1}`; + } else if (where1 === 'FIRST') { + at1 = '0'; + } + + if (where2 === 'FROM_START') { + at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + } else if (where2 === 'FROM_END') { + at2 = Blockly.JavaScript.getAdjusted(block, 'AT2', 0, false, Blockly.JavaScript.ORDER_SUBTRACTION); + at2 = `${text}.length - ${at2}`; + } else if (where2 === 'LAST') { + at2 = `${text}.length`; + } + } else { + const at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + const at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + const getIndex_ = Blockly.JavaScript.text.getIndex_; + const wherePascalCase = { + FIRST : 'First', + LAST : 'Last', + FROM_START: 'FromStart', + FROM_END : 'FromEnd', + }; + const functionName = Blockly.JavaScript.provideFunction_( + `subsequence${wherePascalCase[where1]}${wherePascalCase[where2]}`, + [ + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}( + sequence + ${where1 == 'FROM_END' || where1 == 'FROM_START' ? ', at1' : ''} + ${where2 == 'FROM_END' || where2 == 'FROM_START' ? ', at2' : ''} + ) { + var start = ${getIndex_('sequence', where1, 'at1')}; + var end = ${getIndex_('sequence', where2, 'at2')}; + return sequence.slice(start, end); + }`, + ] + ); + + code = `${functionName}( + ${text} + ${where1 == 'FROM_END' || where1 == 'FROM_START' ? `, ${at1}` : ''} + ${where2 == 'FROM_END' || where2 == 'FROM_START' ? `, ${at2}` : ''} + )`; + } + + code = `${text}.slice(${at1}, ${at2})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_indexOf.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_indexOf.js new file mode 100644 index 0000000000..464c38ee5f --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_indexOf.js @@ -0,0 +1,43 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_indexOf = { + init() { + this.jsonInit({ + message0: translate('in text %1 find %2 occurence of text %3'), + args0 : [ + { + type: 'input_value', + name: 'VALUE', + // check: 'String', Rendering looks off when check is enabled. + }, + { + type : 'field_dropdown', + name : 'END', + options: [[translate('first'), 'FIRST'], [translate('last'), 'LAST']], + }, + { + type: 'input_value', + name: 'FIND', + // check: 'String', + }, + ], + output : 'String', + outputShape : Blockly.OUTPUT_SHAPE_SQUARE, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.text_indexOf = block => { + const functionName = block.getFieldValue('END') === 'FIRST' ? 'indexOf' : 'lastIndexOf'; + const substring = Blockly.JavaScript.valueToCode(block, 'FIND', Blockly.JavaScript.ORDER_NONE) || '\'\''; + const text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + + const code = `${text}.${functionName}(${substring})`; + if (block.workspace.options.oneBasedIndex) { + return [`${code} + 1`, Blockly.JavaScript.ORDER_ADDITION]; + } + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_isEmpty.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_isEmpty.js new file mode 100644 index 0000000000..0147a11c2c --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_isEmpty.js @@ -0,0 +1,27 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_isEmpty = { + init() { + this.jsonInit({ + message0: translate('%1 is empty'), + args0 : [ + { + type: 'input_value', + name: 'VALUE', + }, + ], + output : 'Boolean', + outputShape : Blockly.OUTPUT_SHAPE_HEXAGONAL, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.text_isEmpty = block => { + const text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + + const code = `!${text}.length`; + return [code, Blockly.JavaScript.ORDER_LOGICAL_NOT]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_join.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_join.js new file mode 100644 index 0000000000..8946882672 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_join.js @@ -0,0 +1,123 @@ +import { translate } from '../../../../../../common/i18n'; +import { plusIconDark } from '../../images'; + +Blockly.Blocks.text_join = { + init() { + this.jsonInit({ + message0: translate('set %1 to create text with'), + message1: '%1', + args0 : [ + { + type : 'field_variable', + name : 'VARIABLE', + variable: translate('text'), + }, + ], + args1: [ + { + type: 'input_statement', + name: 'STACK', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + const fieldImage = new Blockly.FieldImage(plusIconDark, 25, 25, '', this.onIconClick.bind(this)); + + this.appendDummyInput('ADD_ICON').appendField(fieldImage); + this.moveInputBefore('ADD_ICON', 'STACK'); + }, + onIconClick() { + if (!this.workspace || this.isInFlyout) { + return; + } + + Blockly.Events.setGroup(true); + + const textBlock = this.workspace.newBlock('text_statement'); + textBlock.setMovable(false); + textBlock.initSvg(); + textBlock.render(); + + const shadowBlock = this.workspace.newBlock('text'); + shadowBlock.setShadow(true); + shadowBlock.setFieldValue('', 'TEXT'); + shadowBlock.initSvg(); + shadowBlock.render(); + + const textInput = textBlock.getInput('TEXT'); + textInput.connection.connect(shadowBlock.outputConnection); + + const connection = this.getLastConnectionInStatement('STACK'); + connection.connect(textBlock.previousConnection); + + Blockly.Events.setGroup(false); + + // TODO: Open editor and focus so user can add string right away? + // const inputField = shadowBlock.getField('TEXT'); + // inputField.showEditor_(); + }, + enforceTextStatementType() { + let currentBlock = this.getInputTargetBlock('STACK'); + + while (currentBlock !== null) { + if (currentBlock.type !== 'text_statement') { + currentBlock.unplug(false); + } + currentBlock = currentBlock.getNextBlock(); + } + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + // Only allow `text_statement` type blocks + const blocksInStatement = this.getBlocksInStatement('STACK'); + blocksInStatement.forEach(block => { + if (block.type !== 'text_statement') { + Blockly.Events.disable(); + block.unplug(); + Blockly.Events.enable(); + } + }); + } + }, +}; + +Blockly.JavaScript.text_join = block => { + const varName = Blockly.JavaScript.variableDB_.getName( + block.getFieldValue('VARIABLE'), + Blockly.Variables.NAME_TYPE + ); + + const elements = []; + + let currentBlock = block.getInputTargetBlock('STACK'); + while (currentBlock !== null) { + const value = Blockly.JavaScript[currentBlock.type](currentBlock); + + if (Array.isArray(value) && value.length === 2) { + elements.push(value[0]); + } else { + elements.push(value); + } + + currentBlock = currentBlock.getNextBlock(); + } + + let code; + + if (elements.length === 0) { + code = `${varName} = '';\n`; + } else { + code = `${varName} = ${elements.join(' + ')};\n`; + } + + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_length.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_length.js new file mode 100644 index 0000000000..6567b5331f --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_length.js @@ -0,0 +1,27 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_length = { + init() { + this.jsonInit({ + message0: translate('length of %1'), + args0 : [ + { + type: 'input_value', + name: 'VALUE', + }, + ], + output : 'Number', + outputShape : Blockly.OUTPUT_SHAPE_ROUND, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.text_length = block => { + const text = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\''; + + const code = `${text}.length`; + return [code, Blockly.JavaScript.ORDER_MEMBER]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_print.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_print.js new file mode 100644 index 0000000000..30323e7a76 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_print.js @@ -0,0 +1,26 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_print = { + init() { + this.jsonInit({ + message0: translate('print %1'), + args0 : [ + { + type: 'input_value', + name: 'TEXT', + }, + ], + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + }, +}; + +Blockly.JavaScript.text_print = block => { + const msg = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_NONE) || '\'\''; + const code = `window.alert(${msg});\n`; + return code; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_prompt_ext.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_prompt_ext.js new file mode 100644 index 0000000000..4ca40068ea --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_prompt_ext.js @@ -0,0 +1,62 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_prompt_ext = { + init() { + this.jsonInit({ + message0: translate('prompt for %1 with message %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'TYPE', + options: [[translate('string'), 'TEXT'], [translate('number'), 'NUMBER']], + }, + { + type: 'input_value', + name: 'TEXT', + }, + ], + output : 'String', + outputShape : Blockly.OUTPUT_SHAPE_SQUARE, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + + // Change shape based on selected type + const typeField = this.getField('TYPE'); + typeField.setValidator(value => { + if (value === 'TEXT') { + this.setOutputShape(Blockly.OUTPUT_SHAPE_SQUARE); + this.setOutput(true, 'String'); + } else if (value === 'NUMBER') { + this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); + this.setOutput(true, 'Number'); + } + this.initSvg(); + this.render(false); + return undefined; + }); + }, +}; + +Blockly.JavaScript.text_prompt_ext = block => { + let msg; + + if (block.getField('TEXT')) { + // Internal message + msg = Blockly.JavaScript.quote_(block.getFieldValue('TEXT')); + } else { + // External message + msg = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_NONE) || '\'\''; + } + + let code; + + if (block.getFieldValue('TYPE') === 'NUMBER') { + code = `parseFloat(window.prompt(${msg}))`; + } else { + code = `window.prompt(${msg})`; + } + + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_statement.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_statement.js new file mode 100644 index 0000000000..f25c47d225 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_statement.js @@ -0,0 +1,54 @@ +import { minusIconDark, lockIconDark } from '../../images'; + +Blockly.Blocks.text_statement = { + init() { + this.jsonInit({ + message0: '%1', + args0 : [ + { + type: 'input_value', + name: 'TEXT', + }, + ], + nextStatement : 'text_statement', + previousStatement: 'text_statement', + colour : Blockly.Colours.BinaryLessGray.colour, + colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, + colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, + previousStatement: null, + nextStatement : null, + }); + + const fieldImage = new Blockly.FieldImage(minusIconDark, 25, 25, '', () => this.onIconClick()); + + this.appendDummyInput('REMOVE_ICON').appendField(fieldImage); + }, + onIconClick() { + if (!this.workspace || this.isInFlyout) { + return; + } + + this.unplug(true); + this.dispose(); + }, + onchange(event) { + if (!this.workspace || this.isInFlyout || this.workspace.isDragging()) { + return; + } + + if (event.type === Blockly.Events.END_DRAG) { + const surroundParent = this.getSurroundParent(); + if (!surroundParent || surroundParent.type !== 'text_join') { + Blockly.Events.disable(); + this.unplug(true); + this.dispose(); + Blockly.Events.enable(); + } + } + }, +}; + +Blockly.JavaScript.text_statement = block => { + const code = Blockly.JavaScript.valueToCode(block, 'TEXT') || ''; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_trim.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_trim.js new file mode 100644 index 0000000000..6a03efa436 --- /dev/null +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_trim.js @@ -0,0 +1,43 @@ +import { translate } from '../../../../../../common/i18n'; + +Blockly.Blocks.text_trim = { + init() { + this.jsonInit({ + message0: translate('trim spaces from %1 of %2'), + args0 : [ + { + type : 'field_dropdown', + name : 'MODE', + options: [ + [translate('both sides'), 'BOTH'], + [translate('left side'), 'LEFT'], + [translate('right side'), 'RIGHT'], + ], + }, + { + type: 'input_value', + name: 'TEXT', + }, + ], + output : 'String', + outputShape : Blockly.OUTPUT_SHAPE_SQUARE, + colour : Blockly.Colours.Binary.colour, + colourSecondary: Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + }); + }, +}; + +Blockly.JavaScript.text_trim = block => { + const operators = { + LEFT : '.replace(/^[\\s\\xa0]+/, \'\')', + RIGHT: '.replace(/[\\s\\xa0]+$/, \'\')', + BOTH : '.trim()', + }; + + const operator = operators[block.getFieldValue('MODE')]; + const text = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_MEMBER) || '\'\''; + + const code = `${text}${operator}`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; +}; From 5cfb9a764875571a8f837da1025e13314a26d094 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:10:52 +0800 Subject: [PATCH 02/74] Update Build scripts --- gulp/build.js | 5 ++++- gulp/bundle.js | 7 +++---- gulp/static.js | 5 +++++ 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/gulp/build.js b/gulp/build.js index 30ebffc806..2deb17ae5e 100644 --- a/gulp/build.js +++ b/gulp/build.js @@ -16,7 +16,8 @@ const getConfig = prefix => ({ binary_style_img: 'image/binary-style', elevio_script : '', - gtm_head : ' ', + gtm_head: + ' ', gtm_iframe: ' ', }); @@ -62,6 +63,7 @@ gulp.task( 'copy-jquery-img', 'copy-binary-style-css', 'copy-binary-style-img', + 'copy-scratch-media', 'copy-js', 'pull-blockly-translations', done => { @@ -82,6 +84,7 @@ gulp.task( 'copy-binary-style-css', 'copy-binary-style-img', 'copy-js', + 'copy-scratch-media', 'pull-blockly-translations' ) ); diff --git a/gulp/bundle.js b/gulp/bundle.js index 03323b1718..4019270ddb 100644 --- a/gulp/bundle.js +++ b/gulp/bundle.js @@ -15,10 +15,9 @@ gulp.task( 'bundle-js', gulp.parallel(done => { gulp.src([ - './node_modules/blockly/blockly_compressed.js', - './node_modules/blockly/blocks_compressed.js', - './node_modules/blockly/javascript_compressed.js', - './node_modules/blockly/msg/messages.js', + './node_modules/scratch-blocks/blockly_compressed_vertical.js', + './node_modules/scratch-blocks/msg/messages.js', + './node_modules/blockly/generators/javascript.js', ]) .pipe(concat('bundle.js')) .pipe(rev()) diff --git a/gulp/static.js b/gulp/static.js index fb2484d7d4..ddf8c532d3 100644 --- a/gulp/static.js +++ b/gulp/static.js @@ -44,3 +44,8 @@ gulp.task( 'copy-binary-style-img', gulp.series(() => gulp.src('node_modules/binary-style/src/images/**').pipe(gulp.dest('www/image/binary-style'))) ); + +gulp.task( + 'copy-scratch-media', + gulp.series(() => gulp.src('node_modules/scratch-blocks/media/**').pipe(gulp.dest('www/image/scratch'))) +); From 4b99149699d8cbf142af71b109a98c00dc910e20 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:11:20 +0800 Subject: [PATCH 03/74] Update package.json --- package.json | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 895c9f81bd..764e653960 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "i18next-scanner": "1.9.4", "immutable": "^3.8.2", "jest": "^24.7.1", - "jquery": "^3.4.0", + "jquery": "3.2.1", "jquery-ui": "1.12.1", "jquery-ui-css": "1.11.4", "js-interpreter": "^1.4.6", @@ -92,6 +92,8 @@ "react-joyride": "^1.11.1", "redux": "^3.7.2", "redux-thunk": "^2.2.0", + "scratch-blocks": "0.1.0-prerelease.1556115881", + "blockly": "github:google/blockly#9c4f9d6", "sha1": "^1.1.1", "trackjs": "^2.10.2", "vinyl-paths": "^2.1.0", @@ -101,7 +103,6 @@ "dependencies": { "@binary-com/smartcharts": "^0.3.9", "binary-style": "^0.2.4", - "blockly": "github:google/blockly#59e5ac6", "commander": "^2.12.2", "concat-stream": "^1.6.0", "core-js": "^2.6.5", @@ -111,6 +112,7 @@ "react": "^16.4.0", "react-dom": "^16.4.0", "react-render-html": "^0.5.2", - "react-transition-group": "^2.3.1" + "react-transition-group": "^2.3.1", + "rxjs-compat": "^6.5.1" } } From 53ed43977ffebc6f1aecec849865f7d1054eb78d Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:11:50 +0800 Subject: [PATCH 04/74] Update const options --- src/botPage/common/const.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/common/const.js b/src/botPage/common/const.js index 844e3fa331..540e63a37c 100644 --- a/src/botPage/common/const.js +++ b/src/botPage/common/const.js @@ -150,7 +150,7 @@ const config = { }, ], }, - barrierTypes: [['Offset +', '+'], ['Offset -', '-']], + barrierTypes: [['Offset\u00A0+', '+'], ['Offset\u00A0-', '-']], ohlcFields : [ [translate('Open'), 'open'], [translate('High'), 'high'], From 7bc7a5b7115b629acd5429fa41af0ea403f097c1 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:12:34 +0800 Subject: [PATCH 05/74] Update CSS + XMLs --- static/css/_blockly-toolbox.scss | 28 +- static/css/_panel.scss | 37 +- static/xml/main.xml | 80 ++- static/xml/toolbox.xml | 999 +++++++++++++++++-------------- 4 files changed, 653 insertions(+), 491 deletions(-) diff --git a/static/css/_blockly-toolbox.scss b/static/css/_blockly-toolbox.scss index a3f8870445..6d72ff6baf 100644 --- a/static/css/_blockly-toolbox.scss +++ b/static/css/_blockly-toolbox.scss @@ -48,10 +48,11 @@ } .blocklyFlyoutBackground { - fill: $white !important; + fill: #fcfcfc !important; fill-opacity: 1 !important; stroke: $brand-dark-gray; - stroke-width: 0.063em; + stroke-width: 0.15em; + stroke-linecap: round; } .blocklyToolboxDiv { @@ -59,7 +60,6 @@ border-width: thin; color: $brand-dark-gray; border-right: 0.063em solid; - width: 11em; } .blocklyIconShape { @@ -71,11 +71,6 @@ opacity: 1 !important; } -.blocklyScrollbarVertical, -.blocklyScrollbarHorizontal { - display: none; -} - .blocklyTreeRow { margin-bottom: 0px !important; height: 1.9em !important; @@ -92,6 +87,19 @@ position: absolute; } -.blocklyMainBackground { - stroke: none; +.scratchCategoryMenu { + width: 100%; + color: #575E75; + font-size: 1em; + user-select: none; } + +.scratchCategoryMenuItem { + padding: 0.5em 1em; + cursor: pointer; + text-align: left; +} + +.scratchCategoryItemBubble { + display: none; +} \ No newline at end of file diff --git a/static/css/_panel.scss b/static/css/_panel.scss index e4b7dcd604..3de64fef1e 100644 --- a/static/css/_panel.scss +++ b/static/css/_panel.scss @@ -320,24 +320,6 @@ $disabled-color: #F2F2F2; .ui-dialog-content { padding: 0px !important; - - &.ui-widget-content { - input[type=text] { - height: 40px; - width: 100%; - } - .description { - font-size: 75%; - margin: 0.5em 0; - } - .input-row { - margin: 1em 0; - - &.last { - margin-bottom: 0; - } - } - } } #load-dialog, #save-dialog { @@ -346,7 +328,23 @@ $disabled-color: #F2F2F2; } } +#load-dialog, #save-dialog, #integrations-dialog { + .input-row { + margin: 1em 0; + } + .input-row.last { + margin-bottom: 0; + } + & .description { + font-size: 75%; + } +} + #save-dialog { + #save-filename { + width: 100%; + height: 40px; + } #collection { padding: 1.2em; border: 1px solid $brand-gray; @@ -369,6 +367,9 @@ $disabled-color: #F2F2F2; font-size: 20px; margin: 0 0 0.2em 0; } + .description { + margin: 0 0 0.2em 0; + } .left { width: 60%; display: table-cell; diff --git a/static/xml/main.xml b/static/xml/main.xml index fb7fb2f874..62d1385104 100644 --- a/static/xml/main.xml +++ b/static/xml/main.xml @@ -1,37 +1,75 @@  - + + + + + forex + smart_fx + WLDAUD + + + callput + risefall + + + both + + + 60 + + + + + FALSE + + + + + + + FALSE + + + + + + + + + + + + + + - + + t USD - + + + 1 + + - - 1 - + + 1 + - + - - - - - - - - - - - + + CALL - + - + - + \ No newline at end of file diff --git a/static/xml/toolbox.xml b/static/xml/toolbox.xml index a8c4658f1c..fc8ef38db6 100644 --- a/static/xml/toolbox.xml +++ b/static/xml/toolbox.xml @@ -1,443 +1,558 @@ + + + + + + + + + + + + + + + + + ADD + + + 1 + + + + + 1 + + + + + ROOT + + + 9 + + + + + SIN + + + 45 + + + + + + + EVEN + + + 0 + + + + + item + + + 1 + + + + + + ROUND + + + 3.1 + + + + + + + 64 + + + + + 10 + + + + + + + 50 + + + + + 1 + + + + + 100 + + + + + + + 1 + + + + + 100 + + + + + + + + abc + + + text + + + + + abc + + + + + + + text + + + abc + + + + + + + abc + + + + + + + + + + + + FIRST + + + text + + + + + abc + + + + + + FROM_START + + + item + + + + + 1 + + + + + + FROM_START + FROM_START + + + text + + + + + 0 + + + + + 2 + + + + + UPPERCASE + + + abc + + + + + BOTH + + + abc + + + + + + + abc + + + + + TEXT + + + abc + + + + + + + list + + + + + + + + + + list + + + 5 + + + + + + + + + + + + SPLIT + + + , + + + + + + + + + + + + + + + + + + + + forex + smart_fx + WLDAUD + + + callput + risefall + + + both + + + 60 + + + + + FALSE + + + + + + + FALSE + + + + + + + + + + + + + + + + + + + AUD + + + + + + + + + + + + + + + + + + + + + + + + + + open + default + + + 1 + + + + + + + + default + + + 1 + + + + + + + + + + + 0 + + + + + + + 2 + + + + + + + 10 + + + + + + + 5 + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + 0 + + + + + + + + + + + + + + + + + + + + + + + + + + + 1 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + success + silent + + + abc + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file From f568a9110d6068c35550573554a0c948accbc0d2 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:15:22 +0800 Subject: [PATCH 06/74] Pull changes from scratch-blocks --- .../blocks/after_purchase/check_result.js | 19 +++++++++-- .../blocks/before_purchase/purchase.js | 2 +- src/botPage/view/blockly/blocks/images.js | 24 ++++++++++--- src/botPage/view/blockly/blocks/index.js | 34 ++++++++++++++----- src/botPage/view/blockly/blocks/shared.js | 31 +++++++++++++++++ .../view/blockly/blocks/trade/tradeOptions.js | 12 ++++--- 6 files changed, 102 insertions(+), 20 deletions(-) diff --git a/src/botPage/view/blockly/blocks/after_purchase/check_result.js b/src/botPage/view/blockly/blocks/after_purchase/check_result.js index 9c2a8320e3..8c6a780c3c 100644 --- a/src/botPage/view/blockly/blocks/after_purchase/check_result.js +++ b/src/botPage/view/blockly/blocks/after_purchase/check_result.js @@ -12,8 +12,23 @@ Blockly.Blocks.contract_check_result = { this.setTooltip(translate('True if the result matches the selection')); this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); }, - onchange: function onchange(ev) { - insideAfterPurchase(this, ev, 'Check Result'); + onchange(event) { + // insideAfterPurchase(this, ev, 'Check Result'); + + if (event.type === Blockly.Events.END_DRAG) { + // Ensure block is in `trade_definition` block + const parentBlock = this.getParent(); + while (parentBlock !== null) { + if (parentBlock.type === 'trade_definition') { + this.setDisabled(false); + return; + } + } + + $.notify('Woops, not in correct parent.'); + this.unplug(); + this.setDisabled(true); + } }, }; Blockly.JavaScript.contract_check_result = block => { diff --git a/src/botPage/view/blockly/blocks/before_purchase/purchase.js b/src/botPage/view/blockly/blocks/before_purchase/purchase.js index 167845a135..7fb72bf38b 100644 --- a/src/botPage/view/blockly/blocks/before_purchase/purchase.js +++ b/src/botPage/view/blockly/blocks/before_purchase/purchase.js @@ -14,7 +14,7 @@ Blockly.Blocks.purchase = { this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); }, onchange: function onchange(ev) { - insideBeforePurchase(this, ev, 'Purchase'); + // insideBeforePurchase(this, ev, 'Purchase'); }, }; Blockly.JavaScript.purchase = block => { diff --git a/src/botPage/view/blockly/blocks/images.js b/src/botPage/view/blockly/blocks/images.js index 9a4436ae94..b2e1099604 100644 --- a/src/botPage/view/blockly/blocks/images.js +++ b/src/botPage/view/blockly/blocks/images.js @@ -1,10 +1,14 @@ -export const defineContract = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAAGXcA1uAAAAAXNSR0IArs4c6QAAAK9JREFUSA3tVUkOgCAMROP/f4PfU4iOaWQKLYmBg71AOksXTAyhGkeKKuECjTSDUwcFxVdNawNgQ10omJNKROqiJbMJsKJZgKgEouuESRYVFVxO35JTnxGDvk9aWQ5GCXfSPbRbsLHysr0lBeMESaKE3hkmbKk230SY9Ulky5rG/SVJU8udPrMU5s7w9bEugUlN886MWiJN86/o2dywFQ17g10b+dlJefH9X0t9X+YETM6gfmoeI+MAAAAASUVORK5CYII='; +export const defineContract = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAAGXcA1uAAAAAXNSR0IArs4c6QAAAK9JREFUSA3tVUkOgCAMROP/f4PfU4iOaWQKLYmBg71AOksXTAyhGkeKKuECjTSDUwcFxVdNawNgQ10omJNKROqiJbMJsKJZgKgEouuESRYVFVxO35JTnxGDvk9aWQ5GCXfSPbRbsLHysr0lBeMESaKE3hkmbKk230SY9Ulky5rG/SVJU8udPrMU5s7w9bEugUlN886MWiJN86/o2dywFQ17g10b+dlJefH9X0t9X+YETM6gfmoeI+MAAAAASUVORK5CYII='; -export const purchase = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAAEgBckRAAAAAXNSR0IArs4c6QAAAvlJREFUaAXtmU1u00AUxxPEGoluKMcgJ8gCARsW9CZ0ReEU5BwFiYpWqtgYiQuQFSygJ2CRLV2E39+ZZ42dGc84XxIwT3p9X//34VfbsZPRaGNaQkpGPNu4yGESGfHUOtnYte0b6G8NdABJu7NoG4KXmi1GwUTACwtYomzp5r9ripP3/KB8zj7q4P4t0z9o9PfJowM0NhD6D9NN+sFm1xb05Rjybe171nKkDBLqfw6yt1Oqjjo3F4XArbli1dfmT7YpgOQGWPanLgjfFXyn6x9sU+QMfhdKxL/1OfSAGreh4vIR643H8pQ4hydRQCAAPv9owJ7AF1YH/bKr+z7FsPMbdBP8ZNNNOuwE+6v0bPILpJLAXsAvUrgSLxsoG4hsgAtoDDe3CoPhW7uFW8yXOffzcxK++ElOfxzwDXcxafAmhvscfjO8opdBgSfwjedqVPxaXbB5A0op5P+GH8ZwqXgwj6RtaO1kiDYJBiJOJop+HuScRZGyLfcUq2p5nJHVgAnrQzepXF/HnMIVnEckt84Os02qSkdfYN/Pq75KnpPQPFVYMZOBBq2B/EaxFVWApga0x2WT8vu64UIyq0EoMdfXeuC3JFZxhP7L7ByZe0Q5tQqmbKBsoGygbOCv2wAfHU/1SQp9Sw0P5nuNJCeFPUicYY7hW8fHqaYOr4dR5UQfSFN1dhJnAD1a/4RF2RsFq8d10Q0ce+LYyYy9RWiulw/R615gIEiOvuASpb/IDeRv7aLxq7r9FgOQbwvY7g2s72ho0vurgjuIfYmPfbMNijGhvsoSnQxKHABW7brDcvkhN23IBVS5otPc4hvgrHa1QW5/CpvR26loHkKuQuuvc/L7+BDO4sT0tiZ6ZL6dSgrr/VGkd54WrdzpYUM4FVJNF1u0CieM7g90CfjoM4Dn8BTu3v6u8LW2jZ3rA1rXlKz0Zy/Ehk7dlma7bkDdmav9cte1m3o0mLgmweugAW6gUHfuau/n/LeZaHLtGu1DXFufIssGygb+kw38ARimnBqQbBHiAAAAAElFTkSuQmCC'; +export const purchase = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAAEgBckRAAAAAXNSR0IArs4c6QAAAvlJREFUaAXtmU1u00AUxxPEGoluKMcgJ8gCARsW9CZ0ReEU5BwFiYpWqtgYiQuQFSygJ2CRLV2E39+ZZ42dGc84XxIwT3p9X//34VfbsZPRaGNaQkpGPNu4yGESGfHUOtnYte0b6G8NdABJu7NoG4KXmi1GwUTACwtYomzp5r9ripP3/KB8zj7q4P4t0z9o9PfJowM0NhD6D9NN+sFm1xb05Rjybe171nKkDBLqfw6yt1Oqjjo3F4XArbli1dfmT7YpgOQGWPanLgjfFXyn6x9sU+QMfhdKxL/1OfSAGreh4vIR643H8pQ4hydRQCAAPv9owJ7AF1YH/bKr+z7FsPMbdBP8ZNNNOuwE+6v0bPILpJLAXsAvUrgSLxsoG4hsgAtoDDe3CoPhW7uFW8yXOffzcxK++ElOfxzwDXcxafAmhvscfjO8opdBgSfwjedqVPxaXbB5A0op5P+GH8ZwqXgwj6RtaO1kiDYJBiJOJop+HuScRZGyLfcUq2p5nJHVgAnrQzepXF/HnMIVnEckt84Os02qSkdfYN/Pq75KnpPQPFVYMZOBBq2B/EaxFVWApga0x2WT8vu64UIyq0EoMdfXeuC3JFZxhP7L7ByZe0Q5tQqmbKBsoGygbOCv2wAfHU/1SQp9Sw0P5nuNJCeFPUicYY7hW8fHqaYOr4dR5UQfSFN1dhJnAD1a/4RF2RsFq8d10Q0ce+LYyYy9RWiulw/R615gIEiOvuASpb/IDeRv7aLxq7r9FgOQbwvY7g2s72ho0vurgjuIfYmPfbMNijGhvsoSnQxKHABW7brDcvkhN23IBVS5otPc4hvgrHa1QW5/CpvR26loHkKuQuuvc/L7+BDO4sT0tiZ6ZL6dSgrr/VGkd54WrdzpYUM4FVJNF1u0CieM7g90CfjoM4Dn8BTu3v6u8LW2jZ3rA1rXlKz0Zy/Ehk7dlma7bkDdmav9cte1m3o0mLgmweugAW6gUHfuau/n/LeZaHLtGu1DXFufIssGygb+kw38ARimnBqQbBHiAAAAAElFTkSuQmCC'; -export const sellContract = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAAEgBckRAAAAAXNSR0IArs4c6QAAA45JREFUaAXtWDFrFUEQzoU0QhpjFVRs/Cc2Sf6AxD4BQSF/QBFtbPwDYici2ImFIqaVlBEEK7FICBaxCFEQRM/v29vZzO7O3dv3uGCEXdg3M998M7O7d7e37+bmZmpt2y4xEPLITABHazpOC0wLwt5wtVKHDGBeFJEg7og+okTaVY5hUgslSQwGFJhb2tZ6mARI3XQ77x/YbzQR9mlMTpc4u3rDoWEFomXtGe5+0zSXg28oCL69IT8r/kwJYocLF0p1yi2ITwl2YiL6+MTq5iQZiUNvtO24GaAywLc+5FfUqp7tFdCXUeulo0bMMfoy+WY8QdUcMU0O/xfPuZ/6fOJtyZH56chABcC94oMXFWyqOlffs2YFXge4hi3gu05gEU3Mj24ZklvDtkUCfgHdfMiBu+cXMnos3QbGZHDsQVyyEs+CYaYh9yzxNaauQF2BWVaATzL6WG01GwMzE4TgfhSdGzKyAUi8z5HvzEKA5Ez08TOkA34jGInCeDT5H2EX6Di9O+k17+d2nW1kwDjz0JL6Pa85xfKRS5BP0T8oV6aSWwYqFoPQHiqoVyUxc5pgwgLnM3loFxNXZJIQATRMULHgf+5596AfKlemmrkIou2gZxfQJ6b/Lfov9CdZVgDAN9B5fLFnANy9TyH7ijyD72pPch5ZeIu7uynjwOGqQrDI1H+zJJ6Jtb6gK2mH1jVnSFcx+0O86qsrUFegrkBdgboC/9MKZK91vPBG/ac88mLEnxmt5Hxjs2kfTDnHTH3M0HlKdNTioYxtXfM7KB6X9gddiJA8OHHgPESx8UCVXbEQaCjg30bnMf+c4TYh1kCX702szTFwLK6ZQRoUIiSPllwNHjOLBw7uFfQf6GxH6LtO635u6lpDOuicCGtzDO6YCxndGWY8SWymswBE6F2XoG0PIN3EIVfQ36G/KkhhUhDrmunUYDFRByU6cvBsL+0Qyh30iV8QkzSRKcki0DKKiVZwgiHXPDrv4W8+74uEUmz6+Ml3RjGxpzTiP/ocL4UC+5HHvgo2rfTx5ROQAC+5K5hfsNOBgLeAzgH/RtftNYzzKd+yweOuIzuRzlE+AUmMaH6QkK3U/Owi3FQi7gH6+xQfssHnzsPGmu5zDfkOwc9QrPNZRGCyD0/9YWpiwYSAWrJlRlfcGhdDo//dPtca5GMGeFuLxR5cc8bSuQ3rXPwvv6mBqtcVqCtQV+Dfr8BfZ6VurZP8obgAAAAASUVORK5CYII='; +export const sellContract = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAAEgBckRAAAAAXNSR0IArs4c6QAAA45JREFUaAXtWDFrFUEQzoU0QhpjFVRs/Cc2Sf6AxD4BQSF/QBFtbPwDYici2ImFIqaVlBEEK7FICBaxCFEQRM/v29vZzO7O3dv3uGCEXdg3M998M7O7d7e37+bmZmpt2y4xEPLITABHazpOC0wLwt5wtVKHDGBeFJEg7og+okTaVY5hUgslSQwGFJhb2tZ6mARI3XQ77x/YbzQR9mlMTpc4u3rDoWEFomXtGe5+0zSXg28oCL69IT8r/kwJYocLF0p1yi2ITwl2YiL6+MTq5iQZiUNvtO24GaAywLc+5FfUqp7tFdCXUeulo0bMMfoy+WY8QdUcMU0O/xfPuZ/6fOJtyZH56chABcC94oMXFWyqOlffs2YFXge4hi3gu05gEU3Mj24ZklvDtkUCfgHdfMiBu+cXMnos3QbGZHDsQVyyEs+CYaYh9yzxNaauQF2BWVaATzL6WG01GwMzE4TgfhSdGzKyAUi8z5HvzEKA5Ez08TOkA34jGInCeDT5H2EX6Di9O+k17+d2nW1kwDjz0JL6Pa85xfKRS5BP0T8oV6aSWwYqFoPQHiqoVyUxc5pgwgLnM3loFxNXZJIQATRMULHgf+5596AfKlemmrkIou2gZxfQJ6b/Lfov9CdZVgDAN9B5fLFnANy9TyH7ijyD72pPch5ZeIu7uynjwOGqQrDI1H+zJJ6Jtb6gK2mH1jVnSFcx+0O86qsrUFegrkBdgboC/9MKZK91vPBG/ac88mLEnxmt5Hxjs2kfTDnHTH3M0HlKdNTioYxtXfM7KB6X9gddiJA8OHHgPESx8UCVXbEQaCjg30bnMf+c4TYh1kCX702szTFwLK6ZQRoUIiSPllwNHjOLBw7uFfQf6GxH6LtO635u6lpDOuicCGtzDO6YCxndGWY8SWymswBE6F2XoG0PIN3EIVfQ36G/KkhhUhDrmunUYDFRByU6cvBsL+0Qyh30iV8QkzSRKcki0DKKiVZwgiHXPDrv4W8+74uEUmz6+Ml3RjGxpzTiP/ocL4UC+5HHvgo2rfTx5ROQAC+5K5hfsNOBgLeAzgH/RtftNYzzKd+yweOuIzuRzlE+AUmMaH6QkK3U/Owi3FQi7gH6+xQfssHnzsPGmu5zDfkOwc9QrPNZRGCyD0/9YWpiwYSAWrJlRlfcGhdDo//dPtca5GMGeFuLxR5cc8bSuQ3rXPwvv6mBqtcVqCtQV+Dfr8BfZ6VurZP8obgAAAAASUVORK5CYII='; -export const finishSign = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAAEgBckRAAAAAXNSR0IArs4c6QAAAa5JREFUaAXtV1tywzAIdDq5Rg/Ui/SGzYl8j1brzqYYidHDYWw60g/iIWABKc6yGOsG+XdaSr/uFLe0dkZkNC25Wt6U7zq7C05zJMH9FofBd0IymZIKR4qYWAyh95LfbCgAxYLwd/e3h+yJm0YQWiurk2V4cXkzaALO5owKAJV7E7gsr9zzQBaBiirVXaV3KYeTbtDVyNOgUIFU9q+CeFj0bBs9sL/k5fhJnSXHOakbnzxmUKH3kl5m0JK1ZQ/fxQBHncqk3Uskg217mX2mHBC4I3APMAB6HglfgXQPXvqaZgXBRTu4PqTT+M+1+00uPtfW+94rRy/cEbgHKJYIY8pRGykLz/7jEkmIslwt8mSzSrtsbznMDBsF7lM0A1Q74V6iagbTYFZgVsC7Ani7sbzjjPrPPhu1o4skv6YPj3edG/jDAOQXjQxgAe+1p0/rXPhfgvAAih+9bJukVgtfNSq9/plb+A6EB9A8QmePCkdG0/AdCA+geYR068hbo0W9pr326Tz+p35qP+SbAVjPHB2dRcOP0ARw1ugwbvgOtFziR0J72T807MSkswKDFfgBlTxsE+QpCHoAAAAASUVORK5CYII='; +export const finishSign = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAAEgBckRAAAAAXNSR0IArs4c6QAAAa5JREFUaAXtV1tywzAIdDq5Rg/Ui/SGzYl8j1brzqYYidHDYWw60g/iIWABKc6yGOsG+XdaSr/uFLe0dkZkNC25Wt6U7zq7C05zJMH9FofBd0IymZIKR4qYWAyh95LfbCgAxYLwd/e3h+yJm0YQWiurk2V4cXkzaALO5owKAJV7E7gsr9zzQBaBiirVXaV3KYeTbtDVyNOgUIFU9q+CeFj0bBs9sL/k5fhJnSXHOakbnzxmUKH3kl5m0JK1ZQ/fxQBHncqk3Uskg217mX2mHBC4I3APMAB6HglfgXQPXvqaZgXBRTu4PqTT+M+1+00uPtfW+94rRy/cEbgHKJYIY8pRGykLz/7jEkmIslwt8mSzSrtsbznMDBsF7lM0A1Q74V6iagbTYFZgVsC7Ani7sbzjjPrPPhu1o4skv6YPj3edG/jDAOQXjQxgAe+1p0/rXPhfgvAAih+9bJukVgtfNSq9/plb+A6EB9A8QmePCkdG0/AdCA+geYR068hbo0W9pr326Tz+p35qP+SbAVjPHB2dRcOP0ARw1ugwbvgOtFziR0J72T807MSkswKDFfgBlTxsE+QpCHoAAAAASUVORK5CYII='; export const caution = `data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAABsklEQV RoQ+1YS07DMBB9VlG2tJuWZVDYU04CN6BHKCfAnACOADcoJyHsiZRtu0m7rYiMItIFldqZ2ONaEc7Wo5d5n7ETK/T8UT3vH5FAaA @@ -14,3 +18,15 @@ XGXY8EbLc/rsJUXTAH4i4UeheKDoR2gBpO7rr7EPf9Yqu9WswBdc5VTabObBLUU+erxaaZlU6nBmevAK u5A6h0Otxi8AKoewrUbd28JajnI12uOThsAjuwlc5mBmiICEfKbBQwH+uicZr9dCbgKVLsyHT+IzskhVykukVGjIB7pOwiI07AMl LWkfFCoAHlR8otMt4I0JGSiYx3AgciJRaZkxD4Gymgy8HEPgDaQqtzoOtLfNZHAj7V5WD33oEfb0MpQATTN7IAAAAASUVORK5CYI I=`; + +export const plusIconDark = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAQAAABIkb+zAAAC+0lEQVR4Ae2bA4xeURCFp7bdoGvjv3OC2m1Q24xW0cZJ3caqzdhJ3Vi1bdu2po1T3v95Nvu+E/N+z5hDMf4RExMTE4N0HmkqsZS34RAu4iG+8kd+hEs4xNuw1FTySKSTRvIbYyLW4xbEHr7JG8z47CakBVOMlXgHcRZ+w6tRSNGCOmY874e4D+83E1CHoqCwBWbzI4gPuY85hS0oVGrwDDyD+JgnZibVoHDgXByCBJBDnBvG8kfiPSSgvOfBFCg1sRASbHhRYIdSSn3eDgk+vD2lPvlPfks+AgknfCS/pd8XzTRchYSYq4Vp5B/ogNuQkHPbdPTv4LkMiSCXclr5curiOCSinMivS17hZZAIs5S8YfpDIs5Ack9OKzyJWoAfozW5BVsgCrKF3GG6QHQk0ZVcUBsXIe5Dv+FJ4aKLlx6UQtQICEodb3++qUmAb1BtcgJPh2gSgGAaOQEX1QlcouQxPSHqBMT0dH791yDg/H5Q1Ig/qhT4UNSIksGMgqgUEDPK+fOnKgFeRsmAM1oFcCap9y+IWgFJ4mXf9NMsYPqRDZRpFkCZXWCJaoElZIP3aBbgPWQDp50v0D2OBU+TDb6pWYBv2gWeqxZ4bhf4rlrgm13gk2qBT2QDTzUL4CnZ4BuaBfhGNbiM6r6R7SYbWFrVHyUqNAtwOdnAQM0CiQFkI7uJZoH8xmSHT2kV4FOUDFiqVQBLq8dnlaJG+KBRgD8WNaLkwGaVApsoWbiHSoEe1eDzuuIfHDzd6S+me6oE7lNtcgZXaBLgchczoZp+s1Jtck6iK0RHTJdqOWrwE7Tmx5CI8wStyT0YGLWA6V+lB554mR8jZ8ciEzjmywRpTitcUj30Z8d0VD52aSeREvbgayIlHj3+83TmbaEsf1tK/eCaG4uUj9/bSQzDO9UFCDucy/sC2fb7ODe8EtB0/0tA4dew5uivYdlfeibyAW9FOJ6EOpFXEXk1v1VeRbR/0zYTHJdBNZLI/KOO+4kf4RIfDqKOGxMTExMT8wNOZdb6YlKT2wAAAABJRU5ErkJggg=='; + +export const plusIconLight = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAQAAABIkb+zAAAC+0lEQVR4Ae2bA4xeURCFp7bdoGvjv3OC2m1Q24xW0cZJ3caqzdhJ3Vi1bdu2po1T3v95Nvu+E/N+z5hDMf4RExMTE4N0HmkqsZS34RAu4iG+8kd+hEs4xNuw1FTySKSTRvIbYyLW4xbEHr7JG8z47CakBVOMlXgHcRZ+w6tRSNGCOmY874e4D+83E1CHoqCwBWbzI4gPuY85hS0oVGrwDDyD+JgnZibVoHDgXByCBJBDnBvG8kfiPSSgvOfBFCg1sRASbHhRYIdSSn3eDgk+vD2lPvlPfks+AgknfCS/pd8XzTRchYSYq4Vp5B/ogNuQkHPbdPTv4LkMiSCXclr5curiOCSinMivS17hZZAIs5S8YfpDIs5Ack9OKzyJWoAfozW5BVsgCrKF3GG6QHQk0ZVcUBsXIe5Dv+FJ4aKLlx6UQtQICEodb3++qUmAb1BtcgJPh2gSgGAaOQEX1QlcouQxPSHqBMT0dH791yDg/H5Q1Ig/qhT4UNSIksGMgqgUEDPK+fOnKgFeRsmAM1oFcCap9y+IWgFJ4mXf9NMsYPqRDZRpFkCZXWCJaoElZIP3aBbgPWQDp50v0D2OBU+TDb6pWYBv2gWeqxZ4bhf4rlrgm13gk2qBT2QDTzUL4CnZ4BuaBfhGNbiM6r6R7SYbWFrVHyUqNAtwOdnAQM0CiQFkI7uJZoH8xmSHT2kV4FOUDFiqVQBLq8dnlaJG+KBRgD8WNaLkwGaVApsoWbiHSoEe1eDzuuIfHDzd6S+me6oE7lNtcgZXaBLgchczoZp+s1Jtck6iK0RHTJdqOWrwE7Tmx5CI8wStyT0YGLWA6V+lB554mR8jZ8ciEzjmywRpTitcUj30Z8d0VD52aSeREvbgayIlHj3+83TmbaEsf1tK/eCaG4uUj9/bSQzDO9UFCDucy/sC2fb7ODe8EtB0/0tA4dew5uivYdlfeibyAW9FOJ6EOpFXEXk1v1VeRbR/0zYTHJdBNZLI/KOO+4kf4RIfDqKOGxMTExMT8wNOZdb6YlKT2wAAAABJRU5ErkJggg=='; + +export const minusIconDark = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAQAAABIkb+zAAAC6ElEQVR4Ae2bA6weURCFp7bd4NnaOyeo3Qa1zaiKXpzUbazajJ3UjVXbtm1rGqO6u/sv5uXf74t91nfvHEpISEhISAgO5PAwU4nlvANHcBmP8Z0/8xNcwRHegeWmkochhzRS0hDjsBF3IHb5Nm8yYwoakRZMBVbjA8Sb/I7XooziBbXMGD4I8S8fNGNRi+KgrBnm8hNIAD7EvLJmFCnVeCpeQAL0mZlG1SgauAhHICF4hIsofHgYPkJC8iMPoFCpjsWQcOUloV1KmXV5JyR8eWdmXQqekuZ8DBKNfKykOQVLWTauQyL0elk2BQfa4S4kYu+a9sFdPFchMXilsEUgty5OQmLyVEltShVeAYnR5ZQapg8kZvuRfwpb4FncAfgpWpJfsA2iwG3kD9MRokOnE/mgJi5DlHjZx6IHMyCKnOH5+PNtTQH4FtUkL/AUiDInkxdwWV2AK+Qe0w2iT9NN//M/mPdBeQP+rDLAp/IG5AYzHKJTM1z796dFXkFuwDmIUs+5Wn9B9OpisW96aw5gepMNzIQodqY9wDLVAZaRDd6nOQDvIxs4C3EjBYzLCGfJBt/WHIBv2wO8VB3gpT3AT9UBftgDfFEd4AvZwHPNAfCcbPAtzQH4Vho8RnW/yPaSDSyv6p8SszUH4FlkA/00B3D6ko2CRqoXNA3JDp+B6JTPkAXtt/Hy9PitUt4AnyD65M/lDcgd2KoywBZyC3dVGaBrGvxeV7zBwVO8bjE9UBXgIdUkb/Bs7d9AFlBL0zYr1STvOJ20BDAd03PUgAgt+SkkZp9Zhj20rw5Mnyo98MQrghg5OxFbgBOBTJAWtsAV7UN/Fkx7/WOXFpzMqAdfncxk9Pgvw987IOHLOzLrhtfcWKJ//N6CMxgftBcgLHARHwjl2B/gouhKQFOCLwFFX8Oap7+GZV/0jONDqRXheDxqxV5F5LX8XnkV0f5P24xVXAZ1j5P3Rx33Cz/BFT4aRh03ISEhISHhF9FSCRjypNzcAAAAAElFTkSuQmCC'; + +export const lockIconDark = + 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGAAAABgCAQAAABIkb+zAAACxklEQVR4Ae2aA4wdURSGT223Udf7Gizn/ieo2zg1Yqe23aC2jag2Yie7QW3bto2pMfehO53duTPJ+f54+A0vDgmCUPzkJamuWMM32P6Z61iruuYlUfBRqbyQz7EdJ+d4gUqloJJWEVPwlu3EwVuelFaRgge34etsFzHXrdZBu/Zr2HYXrAnMfcithQNsuw8O5NYi82SXxx62/y/YHalApon38OAT9mAjz/6a9diDT3EkVpNZVLeYJ3+V+3Bdxytel/vgVKx1VTcyh1WTn7Ot5QOmxHswrA58l20tzw2+CVjMtpaHVuOEL3wGburbYDGZgevinXYyd6y0f26Vwnc0gXeqHpkAU7TTf428Im2Xx6+192AqGaA0P9Su5EgqIjxIU39ApclvVCP96XfxZy2DC86tVSPyGx6jXf/xrrYeqOmPIb/BNucp5OWQC6wGmsBW8hs+6TiBG+QSre16gvxGe4ULyCUodL5B5Df47DiBDa633+hsOZHfaK/wCtcCK517CLaACIiACIiACOQkYwQK+SnbJZynKMSInGQqTrKr8k58Ztu/4DPvzK5KxUOkAo6x7X9wzMOokf60mglWkncQwSdjAp8QIa/wNLYNZhp5BUdNCuAYeYXfmBTgV94FbLMJpYAIiIAIiIAIiIAIiMB1zFItrJpf0xKz+HqoBPACnamUYzel0BkvQiKAtyqfYmBZeBsKAdWb4oBeYRC4lmDKtDSuBl4AYykBGBt4AQYlIF8FXiBSnRIQqV7SAiKQrygBlhV4gcRFAzwmDJ/RshSPsnwt1D8y1TskTQlGzD0Bb0PTmOOOemOOO+JFyJrTPFs157pc11xzWnpkIiACIiACIiACIiACz40KPJdSA8wwKjCDvKJSTZbbcAp5BytCWPCklZwdDV/JmVb0hx1+F/1hh9eiPw1O4eH+lF1yAQ8v2rMvCIIgCF8Av9j925HZ5hYAAAAASUVORK5CYII='; diff --git a/src/botPage/view/blockly/blocks/index.js b/src/botPage/view/blockly/blocks/index.js index 6b5aab0554..1dc06f52e9 100644 --- a/src/botPage/view/blockly/blocks/index.js +++ b/src/botPage/view/blockly/blocks/index.js @@ -1,8 +1,26 @@ -import './tools'; -import './ticks'; -import './before_purchase'; -import './during_purchase'; -import './after_purchase'; -import './indicators'; - -export { default } from './trade'; +// import './tools'; +// import './ticks'; +// import './before_purchase'; +// import './during_purchase'; +// import './after_purchase'; +// import './indicators'; + +import './Scratch/Logic'; +import './Scratch/Math'; +import './Scratch/Text'; +import './Scratch/Advanced/List'; +import './Scratch/Advanced/Variable'; +import './Scratch/Advanced/Functions'; +import './Scratch/Advanced/Loops'; + +import './Scratch/Binary/Tools/Misc.'; +import './Scratch/Binary/Tools/Candle'; +import './Scratch/Binary/Tools/Time'; + +import './Scratch/Binary/Indicators'; +import './Scratch/Binary/Tick Analysis'; + +import './Scratch/Binary/Trade Definition'; +import './Scratch/Binary/Before Purchase'; +import './Scratch/Binary/During Purchase'; +import './Scratch/Binary/After Purchase'; diff --git a/src/botPage/view/blockly/blocks/shared.js b/src/botPage/view/blockly/blocks/shared.js index f0151c106f..a7420ad2b8 100644 --- a/src/botPage/view/blockly/blocks/shared.js +++ b/src/botPage/view/blockly/blocks/shared.js @@ -409,3 +409,34 @@ export const getPredictionForContracts = (contracts, selectedContractType) => { } return predictionRange; }; + +export const pollForContracts = symbol => + new Promise(resolve => { + const contractsForSymbol = haveContractsForSymbol(symbol); + if (!contractsForSymbol) { + // Register an event and use as a lock to avoid spamming API + const event = `contractsLoaded.${symbol}`; + if (!globalObserver.isRegistered(event)) { + globalObserver.register(event, () => {}); + getContractsAvailableForSymbol(symbol).then(contracts => { + globalObserver.unregisterAll(event); // Release the lock + resolve(contracts); + }); + } else { + // Request in progress, start polling localStorage until contracts are available. + const pollingFn = setInterval(() => { + const contracts = haveContractsForSymbol(symbol); + if (contracts) { + clearInterval(pollingFn); + resolve(contracts.available); + } + }, 100); + setTimeout(() => { + clearInterval(pollingFn); + resolve([]); + }, 10000); + } + } else { + resolve(contractsForSymbol.available); + } + }); diff --git a/src/botPage/view/blockly/blocks/trade/tradeOptions.js b/src/botPage/view/blockly/blocks/trade/tradeOptions.js index f3266c543f..4b73a5b912 100644 --- a/src/botPage/view/blockly/blocks/trade/tradeOptions.js +++ b/src/botPage/view/blockly/blocks/trade/tradeOptions.js @@ -18,6 +18,7 @@ export default () => { setInputList(this); this.setPreviousStatement(true, 'TradeOptions'); this.setColour('#f2f2f2'); + this.setInputsInline(false); }, onchange: function onchange(ev) { insideTrade(this, ev, translate('Trade Options')); @@ -151,9 +152,9 @@ export default () => { predictionInput.setVisible(true); // Attach shadow block with API-returned prediction-value (only if user hasn't defined a value) - if (!predictionInput.connection.isConnected()) { - predictionInput.attachShadowBlock(predictionRange[0], 'NUM', 'math_number'); - } + // if (!predictionInput.connection.isConnected()) { + // predictionInput.attachShadowBlock(predictionRange[0], 'NUM', 'math_number'); + // } }); }); }, @@ -176,7 +177,7 @@ export default () => { if (barrierInput) { barrierInput.setVisible(true); if (!barrierInput.connection.isConnected()) { - barrierInput.attachShadowBlock(barrierValue, 'NUM', 'math_number'); + // barrierInput.attachShadowBlock(barrierValue, 'NUM', 'math_number'); } else if (setDefaultValue) { const connectedBlock = barrierInput.connection.targetBlock(); if (connectedBlock.isShadow()) { @@ -268,7 +269,7 @@ export default () => { if (durations.length) { const durationInput = tradeOptionsBlock.getInput('DURATION'); if (!durationInput.connection.isConnected()) { - durationInput.attachShadowBlock(durations[0].minimum, 'NUM', 'math_number'); + // durationInput.attachShadowBlock(durations[0].minimum, 'NUM', 'math_number'); } else if (setMinDuration) { const connectedBlock = durationInput.connection.targetBlock(); const minDuration = durations.find(d => d.unit === selectedDuration); @@ -310,6 +311,7 @@ export default () => { }); }, }; + Blockly.JavaScript.tradeOptions = block => { const durationValue = Blockly.JavaScript.valueToCode(block, 'DURATION', Blockly.JavaScript.ORDER_ATOMIC) || '0'; const durationType = block.getFieldValue('DURATIONTYPE_LIST'); From 6d6d05e03765581895f43029f9a90530537fb9a5 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:15:57 +0800 Subject: [PATCH 07/74] Pull overrides --- src/botPage/view/blockly/overrides/block.js | 76 ++++++ .../view/blockly/overrides/block_svg.js | 33 +++ src/botPage/view/blockly/overrides/colours.js | 29 +++ .../view/blockly/overrides/contextmenu.js | 24 ++ .../view/blockly/overrides/data_category.js | 145 +++++++++++ .../view/blockly/overrides/field_dropdown.js | 29 +++ .../view/blockly/overrides/field_image.js | 240 ++++++++++++++++++ .../view/blockly/overrides/flyout_base.js | 48 ++++ .../view/blockly/overrides/flyout_vertical.js | 99 ++++++++ src/botPage/view/blockly/overrides/icon.js | 26 ++ src/botPage/view/blockly/overrides/index.js | 15 ++ .../view/blockly/overrides/procedures.js | 138 ++++++++++ src/botPage/view/blockly/overrides/toolbox.js | 128 ++++++++++ .../view/blockly/overrides/variables.js | 32 +++ 14 files changed, 1062 insertions(+) create mode 100644 src/botPage/view/blockly/overrides/block.js create mode 100644 src/botPage/view/blockly/overrides/block_svg.js create mode 100644 src/botPage/view/blockly/overrides/colours.js create mode 100644 src/botPage/view/blockly/overrides/contextmenu.js create mode 100644 src/botPage/view/blockly/overrides/data_category.js create mode 100644 src/botPage/view/blockly/overrides/field_dropdown.js create mode 100644 src/botPage/view/blockly/overrides/field_image.js create mode 100644 src/botPage/view/blockly/overrides/flyout_base.js create mode 100644 src/botPage/view/blockly/overrides/flyout_vertical.js create mode 100644 src/botPage/view/blockly/overrides/icon.js create mode 100644 src/botPage/view/blockly/overrides/index.js create mode 100644 src/botPage/view/blockly/overrides/procedures.js create mode 100644 src/botPage/view/blockly/overrides/toolbox.js create mode 100644 src/botPage/view/blockly/overrides/variables.js diff --git a/src/botPage/view/blockly/overrides/block.js b/src/botPage/view/blockly/overrides/block.js new file mode 100644 index 0000000000..4af0628b66 --- /dev/null +++ b/src/botPage/view/blockly/overrides/block.js @@ -0,0 +1,76 @@ +Blockly.Block.prototype.getSiblings = function() { + const siblings = [this]; + ['getPreviousBlock', 'getNextBlock'].forEach(functionName => { + let block = this[functionName](); + while (block !== null) { + const parent = this.getParent(); + if (parent && parent.id === block.id) { + break; + } + + siblings.push(block); + block = block[functionName](); + } + }); + return siblings; +}; + +Blockly.Block.prototype.getChildByType = function(type) { + return this.getDescendants().find(child => child.type === type); +}; + +Blockly.Block.prototype.getChildFieldValue = function(childType, childField) { + const childBlock = this.getChildByType(childType); + if (childBlock) { + const value = childBlock.getFieldValue(childField); + return value; + } + return null; +}; + +Blockly.Block.prototype.childValueToCode = function(childType, childField) { + const childBlock = this.getChildByType(childType); + return childBlock && Blockly.JavaScript.valueToCode(childBlock, childField, Blockly.JavaScript.ORDER_ATOMIC); +}; + +Blockly.Block.prototype.getBlocksInStatement = function(statementInputName) { + const blocksInStatement = []; + const firstBlock = this.getInputTargetBlock(statementInputName); + + if (firstBlock) { + return firstBlock.getSiblings(); + } + return blocksInStatement; +}; + +Blockly.Block.prototype.getLastConnectionInStatement = function(statementInputName) { + const firstBlockInStack = this.getInputTargetBlock(statementInputName); + if (firstBlockInStack) { + return firstBlockInStack.lastConnectionInStack(); + } + const statementInput = this.getInput(statementInputName); + return statementInput.connection; +}; + +Blockly.Block.prototype.isDescendantOf = function(type) { + let parentBlock = this.getParent(); + while (parentBlock !== null) { + if (parentBlock.type === type) { + return true; + } + parentBlock = parentBlock.getParent(); + } + return false; +}; + +Blockly.Block.prototype.getTopParent = function() { + let parent = this.getParent(); + while (parent !== null) { + const nextParent = parent.getParent(); + if (!nextParent) { + return parent; + } + parent = nextParent; + } + return null; +}; diff --git a/src/botPage/view/blockly/overrides/block_svg.js b/src/botPage/view/blockly/overrides/block_svg.js new file mode 100644 index 0000000000..805531535d --- /dev/null +++ b/src/botPage/view/blockly/overrides/block_svg.js @@ -0,0 +1,33 @@ +/** + * Set whether the block is disabled or not. + * @param {boolean} disabled True if disabled. + */ +Blockly.BlockSvg.prototype.setDisabled = function(disabled) { + if (this.disabled != disabled) { + Blockly.BlockSvg.superClass_.setDisabled.call(this, disabled); + if (this.rendered) { + this.updateDisabled(); + } + } +}; + +/** + * Enable or disable a block. + */ +Blockly.BlockSvg.prototype.updateDisabled = function() { + if (this.disabled || this.getInheritedDisabled()) { + const added = Blockly.utils.addClass(/** @type {!Element} */ (this.svgGroup_), 'blocklyDisabled'); + if (added) { + this.svgPath_.setAttribute('fill', `url(#${this.workspace.options.disabledPatternId})`); + } + } else { + const removed = Blockly.utils.removeClass(/** @type {!Element} */ (this.svgGroup_), 'blocklyDisabled'); + if (removed) { + this.updateColour(); + } + } + const children = this.getChildren(false); + for (var i = 0, child; (child = children[i]); i++) { + child.updateDisabled(); + } +}; diff --git a/src/botPage/view/blockly/overrides/colours.js b/src/botPage/view/blockly/overrides/colours.js new file mode 100644 index 0000000000..5481babc19 --- /dev/null +++ b/src/botPage/view/blockly/overrides/colours.js @@ -0,0 +1,29 @@ +Blockly.Colours.Binary = { + colour : '#dedede', + colourSecondary: '#e2e2e2', + colourTertiary : '#bababa', +}; + +Blockly.Colours.BinaryLessGray = { + colour : '#f3f3f3', + colourSecondary: '#e2e2e2', + colourTertiary : '#bababa', +}; + +Blockly.Colours.BinaryPurple = { + colour : '#dedede', + colourSecondary: '#e2e2e2', + colourTertiary : '#bababa', +}; + +Blockly.Colours.BinaryLessPurple = { + colour : '#dedede', + colourSecondary: '#e2e2e2', + colourTertiary : '#bababa', +}; + +Blockly.Colours.BinaryProcedures = { + colour : '#1876d2', + colourSecondary: '#0d47a1', + colourTertiary : '#0d47a1', +}; diff --git a/src/botPage/view/blockly/overrides/contextmenu.js b/src/botPage/view/blockly/overrides/contextmenu.js new file mode 100644 index 0000000000..a8750bfae1 --- /dev/null +++ b/src/botPage/view/blockly/overrides/contextmenu.js @@ -0,0 +1,24 @@ +Blockly.ContextMenu.blockDeleteOption = function(block) { + // Option to delete this block but not blocks lower in the stack. + // Count the number of blocks that are nested in this block, + // ignoring shadows and without ordering. + let descendantCount = block.getDescendants(false, true).length; + const nextBlock = block.getNextBlock(); + if (nextBlock) { + // Blocks in the current stack would survive this block's deletion. + descendantCount -= nextBlock.getDescendants(false, true).length; + } + const deleteOption = { + text: + descendantCount == 1 + ? Blockly.Msg.DELETE_BLOCK + : Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(descendantCount)), + enabled: true, + callback() { + Blockly.Events.setGroup(true); + block.dispose(true, true); + Blockly.Events.setGroup(false); + }, + }; + return deleteOption; +}; diff --git a/src/botPage/view/blockly/overrides/data_category.js b/src/botPage/view/blockly/overrides/data_category.js new file mode 100644 index 0000000000..eebc8efb42 --- /dev/null +++ b/src/botPage/view/blockly/overrides/data_category.js @@ -0,0 +1,145 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2017 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 Data Flyout components including variable and list blocks. + * @author marisaleung@google.com (Marisa Leung) + */ + +import { translate } from '../../../../common/i18n'; + +/** + * @name Blockly.DataCategory + * @namespace + * */ +goog.provide('Blockly.DataCategory'); + +goog.require('Blockly.Blocks'); +goog.require('Blockly.VariableModel'); +goog.require('Blockly.Variables'); +goog.require('Blockly.Workspace'); + +/** + * Construct the blocks required by the flyout for the variable category. + * @param {!Blockly.Workspace} workspace The workspace containing variables. + * @return {!Array.} Array of XML block elements. + */ +Blockly.DataCategory = function(workspace) { + const variableModelList = workspace.getVariablesOfType(''); + const xmlList = []; + + // Create Variable + Blockly.DataCategory.addCreateButton(xmlList, workspace, 'VARIABLE'); + + if (variableModelList.length > 0) { + const generateVariableFieldXmlString = variableModel => { + // The variable name may be user input, so it may contain characters that + // need to be escaped to create valid XML. + const escapedText = `${goog.string.htmlEscape(variableModel.name)}`; + return escapedText; + }; + + // TEMP + const operationsLabel = document.createElement('label'); + operationsLabel.setAttribute('text', translate('variables_set')); + xmlList.push(operationsLabel); + + const firstVariable = variableModelList[0]; + + // Create 'Set `var` to'-block + if (Blockly.Blocks.variables_set) { + const gap = Blockly.Blocks.math_change ? 8 : 24; + const setBlockText = + `${'' + ``}${generateVariableFieldXmlString( + firstVariable + )}` + ''; + const setBlock = Blockly.Xml.textToDom(setBlockText).firstChild; + xmlList.push(setBlock); + } + + // TEMP + const changeLabel = document.createElement('label'); + changeLabel.setAttribute('text', translate('math_change')); + xmlList.push(changeLabel); + + // Create 'Change `var` by `1`'-block + if (Blockly.Blocks.math_change) { + const gap = Blockly.Blocks.variables_get ? 20 : 8; + const changeBlockText = + `${'' + ``}${generateVariableFieldXmlString( + firstVariable + )}` + + '' + + '1' + + '' + + '' + + '' + + ''; + const changeBlock = Blockly.Xml.textToDom(changeBlockText).firstChild; + xmlList.push(changeBlock); + } + + const variablesLabel = document.createElement('label'); + variablesLabel.setAttribute('text', translate('variable_get')); + xmlList.push(variablesLabel); + + // Create variable_get block for each variable + if (Blockly.Blocks.variables_get) { + variableModelList.sort(Blockly.VariableModel.compareByName); + + for (var i = 0, variable; (variable = variableModelList[i]); i++) { + const getBlockText = + `${'' + ''}${generateVariableFieldXmlString(variable)}` + + ''; + + const getBlock = Blockly.Xml.textToDom(getBlockText).firstChild; + xmlList.push(getBlock); + } + } + } + + return xmlList; +}; + +/** + * Construct a create variable button and push it to the xmlList. + * @param {!Array.} xmlList Array of XML block elements. + * @param {Blockly.Workspace} workspace Workspace to register callback to. + * @param {string} type Type of variable this is for. For example, 'LIST' or + * 'VARIABLE'. + */ +Blockly.DataCategory.addCreateButton = function(xmlList, workspace, type) { + const button = goog.dom.createDom('button'); + // Set default msg, callbackKey, and callback values for type 'VARIABLE' + const msg = Blockly.Msg.NEW_VARIABLE; + const callbackKey = 'CREATE_VARIABLE'; + const callback = function(button) { + const workspace = button.getTargetWorkspace(); + Blockly.Variables.createVariable(workspace, null, ''); + workspace.toolbox_.showCategory_('Variables'); + }; + + button.setAttribute('text', msg); + button.setAttribute('callbackKey', callbackKey); + workspace.registerButtonCallback(callbackKey, callback); + xmlList.push(button); +}; diff --git a/src/botPage/view/blockly/overrides/field_dropdown.js b/src/botPage/view/blockly/overrides/field_dropdown.js new file mode 100644 index 0000000000..bb1625a469 --- /dev/null +++ b/src/botPage/view/blockly/overrides/field_dropdown.js @@ -0,0 +1,29 @@ +import { hideEventsFromBlockly } from '../utils'; + +Blockly.FieldDropdown.prototype.updateOptions = function(options, opt_default = null, triggerEvent = true) { + const previousValue = this.getValue(); + + this.menuGenerator_ = options; + if (opt_default && this.menuGenerator_.findIndex(item => item[1] === opt_default) !== -1) { + hideEventsFromBlockly(() => { + this.setValue(''); + this.setValue(opt_default); + }); + } else if (this.menuGenerator_.length > 0) { + hideEventsFromBlockly(() => { + this.setValue(''); + this.setValue(this.menuGenerator_[0][1]); + }); + } + + if (triggerEvent) { + const event = new Blockly.Events.BlockChange( + this.sourceBlock_, + 'field', + this.name, + previousValue, + this.getValue() + ); + Blockly.Events.fire(event); + } +}; diff --git a/src/botPage/view/blockly/overrides/field_image.js b/src/botPage/view/blockly/overrides/field_image.js new file mode 100644 index 0000000000..564e91ab44 --- /dev/null +++ b/src/botPage/view/blockly/overrides/field_image.js @@ -0,0 +1,240 @@ +/** + * @license + * Visual Blocks Editor + * + * Copyright 2012 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 Image field. Used for pictures, icons, etc. + * @author fraser@google.com (Neil Fraser) + */ + +goog.provide('Blockly.FieldImage'); + +goog.require('Blockly.Field'); +goog.require('Blockly.utils'); + +goog.require('goog.math.Size'); + +/** + * Class for an image on a block. + * @param {string} src The URL of the image. + * @param {number} width Width of the image. + * @param {number} height Height of the image. + * @param {string=} opt_alt Optional alt text for when block is collapsed. + * @param {Function=} opt_onClick Optional function to be called when the image + * is clicked. If opt_onClick is defined, opt_alt must also be defined. + * @param {boolean=} opt_flipRtl Whether to flip the icon in RTL. + * @extends {Blockly.Field} + * @constructor + */ +Blockly.FieldImage = function(src, width, height, opt_alt, opt_onClick, opt_flipRtl) { + this.sourceBlock_ = null; + + // Ensure height and width are numbers. Strings are bad at math. + this.height_ = Number(height); + this.width_ = Number(width); + this.size_ = new goog.math.Size(this.width_, this.height_); + this.flipRtl_ = opt_flipRtl; + this.tooltip_ = ''; + this.setValue(src); + this.setText(opt_alt); + + if (typeof opt_onClick === 'function') { + this.clickHandler_ = opt_onClick; + } +}; +goog.inherits(Blockly.FieldImage, Blockly.Field); + +/** + * Construct a FieldImage from a JSON arg object, + * dereferencing any string table references. + * @param {!Object} options A JSON object with options (src, width, height, + * alt, and flipRtl). + * @return {!Blockly.FieldImage} The new field instance. + * @package + * @nocollapse + */ +Blockly.FieldImage.fromJson = function(options) { + const src = Blockly.utils.replaceMessageReferences(options.src); + const width = Number(Blockly.utils.replaceMessageReferences(options.width)); + const height = Number(Blockly.utils.replaceMessageReferences(options.height)); + const alt = Blockly.utils.replaceMessageReferences(options.alt); + const flipRtl = !!options.flipRtl; + return new Blockly.FieldImage(src, width, height, alt, null, flipRtl); +}; + +/** + * Editable fields are saved by the XML renderer, non-editable fields are not. + */ +Blockly.FieldImage.prototype.EDITABLE = false; + +/** + * Install this image on a block. + */ +Blockly.FieldImage.prototype.init = function() { + if (this.fieldGroup_) { + // Image has already been initialized once. + return; + } + // Build the DOM. + /** @type {SVGElement} */ + this.fieldGroup_ = Blockly.utils.createSvgElement('g', {}, null); + if (!this.visible_) { + this.fieldGroup_.style.display = 'none'; + } + /** @type {SVGElement} */ + this.imageElement_ = Blockly.utils.createSvgElement( + 'image', + { + height: `${this.height_}px`, + width : `${this.width_}px`, + }, + this.fieldGroup_ + ); + this.setValue(this.src_); + this.setText(this.text_); + this.sourceBlock_.getSvgRoot().appendChild(this.fieldGroup_); + + if (this.tooltip_) { + this.imageElement_.tooltip = this.tooltip_; + } else { + // Configure the field to be transparent with respect to tooltips. + this.setTooltip(this.sourceBlock_); + } + Blockly.Tooltip.bindMouseEvents(this.imageElement_); + + this.maybeAddClickHandler_(); +}; + +/** + * Dispose of all DOM objects belonging to this text. + */ +Blockly.FieldImage.prototype.dispose = function() { + goog.dom.removeNode(this.fieldGroup_); + this.fieldGroup_ = null; + this.imageElement_ = null; +}; + +/** + * Bind events for a mouse down on the image, but only if a click handler has + * been defined. + * @private + */ +Blockly.FieldImage.prototype.maybeAddClickHandler_ = function() { + if (this.clickHandler_) { + this.mouseDownWrapper_ = Blockly.bindEventWithChecks_(this.fieldGroup_, 'mousedown', this, this.clickHandler_); + // pxtblockly & binary-bot: if a click handler is attached to the image, change the cursor to a pointer + if (this.imageElement_) this.imageElement_.style.cursor = 'pointer'; + } +}; + +/** + * Change the tooltip text for this field. + * @param {string|!Element} newTip Text for tooltip or a parent element to + * link to for its tooltip. + */ +Blockly.FieldImage.prototype.setTooltip = function(newTip) { + this.tooltip_ = newTip; + if (this.imageElement_) { + this.imageElement_.tooltip = newTip; + } +}; + +/** + * Get the source URL of this image. + * @return {string} Current text. + * @override + */ +Blockly.FieldImage.prototype.getValue = function() { + return this.src_; +}; + +/** + * Set the source URL of this image. + * @param {?string} src New source. + * @override + */ +Blockly.FieldImage.prototype.setValue = function(src) { + if (src === null) { + // No change if null. + return; + } + this.src_ = src; + if (this.imageElement_) { + this.imageElement_.setAttributeNS('http://www.w3.org/1999/xlink', 'xlink:href', src || ''); + } +}; + +/** + * Get whether to flip this image in RTL + * @return {boolean} True if we should flip in RTL. + */ +Blockly.FieldImage.prototype.getFlipRtl = function() { + return this.flipRtl_; +}; + +/** + * Set the alt text of this image. + * @param {?string} alt New alt text. + * @override + */ +Blockly.FieldImage.prototype.setText = function(alt) { + if (alt === null) { + // No change if null. + return; + } + this.text_ = alt; + if (this.imageElement_) { + this.imageElement_.setAttribute('alt', alt || ''); + } +}; + +/** + * Images are fixed width, no need to render. + * @private + */ +Blockly.FieldImage.prototype.render_ = function() { + // NOP +}; + +/** + * Images are fixed width, no need to render even if forced. + */ +Blockly.FieldImage.prototype.forceRerender = function() { + // NOP +}; + +/** + * Images are fixed width, no need to update. + * @private + */ +Blockly.FieldImage.prototype.updateWidth = function() { + // NOP +}; + +/** + * If field click is called, and click handler defined, + * call the handler. + */ +Blockly.FieldImage.prototype.showEditor_ = function() { + if (this.clickHandler_) { + this.clickHandler_(this); + } +}; + +Blockly.Field.register('field_image', Blockly.FieldImage); diff --git a/src/botPage/view/blockly/overrides/flyout_base.js b/src/botPage/view/blockly/overrides/flyout_base.js new file mode 100644 index 0000000000..43c243b768 --- /dev/null +++ b/src/botPage/view/blockly/overrides/flyout_base.js @@ -0,0 +1,48 @@ +/** + * Margin around the edges of the blocks in the flyout. + * @type {number} + * @const + */ +Blockly.Flyout.prototype.MARGIN = 24; + +/** + * Update the view based on coordinates calculated in position(). + * @param {number} width The computed width of the flyout's SVG group + * @param {number} height The computed height of the flyout's SVG group. + * @param {number} x The computed x origin of the flyout's SVG group. + * @param {number} y The computed y origin of the flyout's SVG group. + * @protected + * binary-bot: Imported from Blockly, used in flyout_vertical.js + */ +Blockly.Flyout.prototype.positionAt_ = function(width, height, x, y) { + this.svgGroup_.setAttribute('width', width); + this.svgGroup_.setAttribute('height', height); + if (this.svgGroup_.tagName == 'svg') { + var transform = `translate(${x}px,${y}px)`; + Blockly.utils.setCssTransform(this.svgGroup_, transform); + } else { + // IE and Edge don't support CSS transforms on SVG elements so + // it's important to set the transform on the SVG element itself + var transform = `translate(${x},${y})`; + this.svgGroup_.setAttribute('transform', transform); + } + + // Update the scrollbar (if one exists). + if (this.scrollbar_) { + // Set the scrollbars origin to be the top left of the flyout. + this.scrollbar_.setOrigin(x, y); + this.scrollbar_.resize(); + // Set the position again so that if the metrics were the same (and the + // resize failed) our position is still updated. + this.scrollbar_.setPosition_(this.scrollbar_.position_.x, this.scrollbar_.position_.y); + } +}; + +/** + * Get the width of the flyout. + * @return {number} The width of the flyout. + * binary-bot: Return actual width rather than this.DEFAULT_WIDTH. + */ +Blockly.Flyout.prototype.getWidth = function() { + return this.width_; +}; diff --git a/src/botPage/view/blockly/overrides/flyout_vertical.js b/src/botPage/view/blockly/overrides/flyout_vertical.js new file mode 100644 index 0000000000..dc284b6720 --- /dev/null +++ b/src/botPage/view/blockly/overrides/flyout_vertical.js @@ -0,0 +1,99 @@ +/** + * Move the flyout to the edge of the workspace. + */ +Blockly.VerticalFlyout.prototype.position = function() { + if (!this.isVisible()) { + return; + } + const targetWorkspaceMetrics = this.targetWorkspace_.getMetrics(); + if (!targetWorkspaceMetrics) { + // Hidden components will return null. + return; + } + // Record the height for Blockly.Flyout.getMetrics_ + // binary-bot: -20 so it's unattached to bottom. + this.height_ = targetWorkspaceMetrics.viewHeight - 20; + + const edgeWidth = this.width_ - this.CORNER_RADIUS; + // binary-bot: use this.height_ instead of targetWorkspaceMetrics.veiwHeight + const edgeHeight = this.height_ - 2 * this.CORNER_RADIUS; + this.setBackgroundPath_(edgeWidth, edgeHeight); + + // Y is always 0 since this is a vertical flyout. + // binary-bot: We want some spacing between top and toolbox. + const y = 10; + // If this flyout is the toolbox flyout. + if (this.targetWorkspace_.toolboxPosition == this.toolboxPosition_) { + // If there is a category toolbox. + if (targetWorkspaceMetrics.toolboxWidth) { + if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { + var x = targetWorkspaceMetrics.toolboxWidth - 100; // binary-bot: Dirty spacing, but it works. TODO, calc properly. + } else { + var x = targetWorkspaceMetrics.viewWidth - this.width_; + } + } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { + var x = 0; + } else { + var x = targetWorkspaceMetrics.viewWidth; + } + } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { + var x = 0; + } else { + // Because the anchor point of the flyout is on the left, but we want + // to align the right edge of the flyout with the right edge of the + // blocklyDiv, we calculate the full width of the div minus the width + // of the flyout. + var x = targetWorkspaceMetrics.viewWidth + targetWorkspaceMetrics.absoluteLeft - this.width_; + } + this.positionAt_(this.width_, this.height_, x, y); +}; + +/** + * Compute width of flyout. Position mat under each block. + * For RTL: Lay out the blocks and buttons to be right-aligned. + * binary-bot: Imported from Blockly to allow for dynamic width flyout. + * @private + */ +Blockly.VerticalFlyout.prototype.reflowInternal_ = function() { + this.workspace_.scale = this.targetWorkspace_.scale; + let flyoutWidth = 0; + const blocks = this.workspace_.getTopBlocks(false); + for (var i = 0, block; (block = blocks[i]); i++) { + let width = block.getHeightWidth().width; + if (block.outputConnection) { + width -= 8; // binary-bot: 8 is the original Blockly.BlockSvg.TAB_WIDTH + } + flyoutWidth = Math.max(flyoutWidth, width); + } + for (var i = 0, button; (button = this.buttons_[i]); i++) { + flyoutWidth = Math.max(flyoutWidth, button.width); + } + flyoutWidth += this.MARGIN * 1.5 + 8; // binary-bot: 8 is the original Blockly.BlockSvg.TAB_WIDTH + flyoutWidth *= this.workspace_.scale; + flyoutWidth += Blockly.Scrollbar.scrollbarThickness; + + if (this.width_ != flyoutWidth) { + for (var i = 0, block; (block = blocks[i]); i++) { + if (this.RTL) { + // With the flyoutWidth known, right-align the blocks. + const oldX = block.getRelativeToSurfaceXY().x; + const newX = flyoutWidth / this.workspace_.scale - this.MARGIN - Blockly.BlockSvg.TAB_WIDTH; + block.moveBy(newX - oldX, 0); + } + // if (block.flyoutRect_) { + // this.moveRectToBlock_(block.flyoutRect_, block); + // } + } + if (this.RTL) { + // With the flyoutWidth known, right-align the buttons. + for (var i = 0, button; (button = this.buttons_[i]); i++) { + const y = button.getPosition().y; + const x = flyoutWidth / this.workspace_.scale - button.width - this.MARGIN - Blockly.BlockSvg.TAB_WIDTH; + button.moveTo(x, y); + } + } + // Record the width for .getMetrics_ and .position. + this.width_ = flyoutWidth; + this.position(); + } +}; diff --git a/src/botPage/view/blockly/overrides/icon.js b/src/botPage/view/blockly/overrides/icon.js new file mode 100644 index 0000000000..f7063d22fe --- /dev/null +++ b/src/botPage/view/blockly/overrides/icon.js @@ -0,0 +1,26 @@ +/** + * Render the icon. + * @param {number} cursorX Horizontal offset at which to position the icon. + * @return {number} Horizontal offset for next item to draw. + */ +Blockly.Icon.prototype.renderIcon = function(cursorX) { + if (this.collapseHidden && this.block_.isCollapsed()) { + this.iconGroup_.setAttribute('display', 'none'); + return cursorX; + } + this.iconGroup_.setAttribute('display', 'block'); + + const TOP_MARGIN = 9; + const width = this.SIZE; + if (this.block_.RTL) { + cursorX -= width; + } + this.iconGroup_.setAttribute('transform', `translate(${cursorX},${TOP_MARGIN})`); + this.computeIconLocation(); + if (this.block_.RTL) { + cursorX -= Blockly.BlockSvg.SEP_SPACE_X; + } else { + cursorX += width + Blockly.BlockSvg.SEP_SPACE_X; + } + return cursorX; +}; diff --git a/src/botPage/view/blockly/overrides/index.js b/src/botPage/view/blockly/overrides/index.js new file mode 100644 index 0000000000..13bed964c7 --- /dev/null +++ b/src/botPage/view/blockly/overrides/index.js @@ -0,0 +1,15 @@ +/* eslint-disable */ +import './block'; +import './block_svg'; +import './colours'; +import './contextmenu'; +import './data_category'; +import './field_dropdown'; +import './field_image'; +import './flyout_base'; +import './flyout_vertical'; +import './icon'; +import './procedures'; +import './toolbox'; +import './variables'; +/* eslint-enable */ diff --git a/src/botPage/view/blockly/overrides/procedures.js b/src/botPage/view/blockly/overrides/procedures.js new file mode 100644 index 0000000000..4cad6b8d0d --- /dev/null +++ b/src/botPage/view/blockly/overrides/procedures.js @@ -0,0 +1,138 @@ +import { translate } from '../../../../common/i18n'; + +/** + * Construct the blocks required by the flyout for the procedure category. + * @param {!Blockly.Workspace} workspace The workspace containing procedures. + * @return {!Array.} Array of XML block elements. + */ +Blockly.Procedures.flyoutCategory = function(workspace) { + const xmlList = []; + + if (Blockly.Blocks.procedures_defnoreturn) { + const label = document.createElement('label'); + label.setAttribute('text', translate('procedures_defnoreturn')); + xmlList.push(label); + + // + // do something + // + var block = document.createElement('block'); + block.setAttribute('type', 'procedures_defnoreturn'); + block.setAttribute('gap', 16); + + // TEMP + var nameField = document.createElement('field'); + nameField.setAttribute('name', 'NAME'); + nameField.appendChild(document.createTextNode(translate('do something'))); + + block.appendChild(nameField); + xmlList.push(block); + } + + if (Blockly.Blocks.procedures_defreturn) { + const label = document.createElement('label'); + label.setAttribute('text', translate('procedures_defreturn')); // TEMP + label.setAttribute('web-class', 'test'); + xmlList.push(label); + + // + // do something + // + var block = document.createElement('block'); + block.setAttribute('type', 'procedures_defreturn'); + block.setAttribute('gap', 16); + var nameField = document.createElement('field'); + nameField.setAttribute('name', 'NAME'); + nameField.appendChild(document.createTextNode(translate('do something'))); + block.appendChild(nameField); + xmlList.push(block); + } + + if (Blockly.Blocks.procedures_ifreturn) { + const label = document.createElement('label'); + label.setAttribute('text', translate('procedures_ifreturn')); // TEMP + xmlList.push(label); + + // + var block = document.createElement('block'); + block.setAttribute('type', 'procedures_ifreturn'); + block.setAttribute('gap', 16); + xmlList.push(block); + } + + if (xmlList.length) { + // Add slightly larger gap between system blocks and user calls. + xmlList[xmlList.length - 1].setAttribute('gap', 24); + } + + function populateProcedures(procedureList, templateName) { + for (let i = 0; i < procedureList.length; i++) { + const name = procedureList[i][0]; + const args = procedureList[i][1]; + // + // + // + // + // + const block = document.createElement('block'); + block.setAttribute('type', templateName); + block.setAttribute('gap', 16); + + const mutation = document.createElement('mutation'); + mutation.setAttribute('name', name); + block.appendChild(mutation); + + for (let j = 0; j < args.length; j++) { + const arg = document.createElement('arg'); + arg.setAttribute('name', args[j]); + mutation.appendChild(arg); + } + + xmlList.push(block); + } + } + + const tuple = Blockly.Procedures.allProcedures(workspace); + populateProcedures(tuple[0], 'procedures_callnoreturn'); + populateProcedures(tuple[1], 'procedures_callreturn'); + return xmlList; +}; + +/** + * Find the definition block for the named procedure. + * @param {string} name Name of procedure. + * @param {!Blockly.Workspace} workspace The workspace to search. + * @return {Blockly.Block} The procedure definition block, or null not found. + */ +Blockly.Procedures.getDefinition = function(name, workspace) { + // Assume that a procedure definition is a top block. + const blocks = workspace.getTopBlocks(false); + for (let i = 0; i < blocks.length; i++) { + if (blocks[i].getProcedureDef) { + const tuple = blocks[i].getProcedureDef(); + if (tuple && Blockly.Names.equals(tuple[0], name)) { + return blocks[i]; + } + } + } + return null; +}; + +// Scratch has a broken version where they return `false` if Blockly.Names.equals(procName[0], name). +// https://github.com/LLK/scratch-blocks/pull/1930 +Blockly.Procedures.isNameUsed = function(name, workspace, opt_exclude) { + const blocks = workspace.getAllBlocks(false); + // Iterate through every block and check the name. + for (let i = 0; i < blocks.length; i++) { + if (blocks[i] == opt_exclude) { + continue; + } + if (blocks[i].getProcedureDef) { + const procName = blocks[i].getProcedureDef(); + if (Blockly.Names.equals(procName[0], name)) { + return true; + } + } + } + return false; +}; diff --git a/src/botPage/view/blockly/overrides/toolbox.js b/src/botPage/view/blockly/overrides/toolbox.js new file mode 100644 index 0000000000..7cea3b68c0 --- /dev/null +++ b/src/botPage/view/blockly/overrides/toolbox.js @@ -0,0 +1,128 @@ +// /** +// * Fill the toolbox with categories and blocks. +// * @param {!Node} newTree DOM tree of blocks. +// * @private +// */ +Blockly.Toolbox.prototype.populate_ = function(newTree) { + this.categoryMenu_.populate(newTree); +}; + +/** + * Show blocks for specific category in flyout + * This is different from Scratch as they show everything in one single flyout. + * @private + */ +Blockly.Toolbox.prototype.showCategory_ = function(categoryName) { + let allContents = []; + + const category = this.categoryMenu_.categories_.find(category => category.name_ === categoryName); + if (!category) { + return; + } + + // const labelString = '' + + // `' + + // ''; + + // const labelXML = Blockly.Xml.textToDom(labelString); + + // allContents.push(labelXML.firstChild); + allContents = allContents.concat(category.getContents()); + + // TESTING ONLY: Generate labels for each block for QA + let newAllContents = []; + if (Array.isArray(allContents) && allContents.length > 1) { + allContents.forEach((node, index) => { + if (node.nodeName === 'block') { + const type = node.getAttribute('type'); + + const labelString = + '' + + `' + + ''; + + const labelXml = Blockly.Xml.textToDom(labelString); + + newAllContents.push(...[labelXml.firstChild, node]); + } + }); + } else { + newAllContents = allContents; + } + // /TESTING ONLY + + this.flyout_.autoClose = true; + this.flyout_.show(newAllContents); +}; + +/** + * Opens the selected category + * binary-bot: unlike in Scratch, we want to have category-specific flyouts. + * @param {Blockly.Toolbox.Category} item The category to select. + * @param {boolean=} opt_shouldScroll Whether to scroll to the selected category. Unused in Binary Bot. + */ +Blockly.Toolbox.prototype.setSelectedItem = function(item, opt_shouldScroll) { + if (this.selectedItem_) { + // They selected a different category but one was already open. Close it. + this.selectedItem_.setSelected(false); + } + this.selectedItem_ = item; + if (this.selectedItem_ != null) { + this.selectedItem_.setSelected(true); + // Scroll flyout to the top of the selected category + const categoryId = item.id_; + this.showCategory_(categoryId); + } else { + this.flyout_.hide(); + } +}; + +/** + * Update the flyout's contents without closing it. Should be used in response + * to a change in one of the dynamic categories, such as variables or + * procedures. + * binary-bot: We don't want to showAll_() cause it'll populate the entire + * flyout with all available blocks. This method is called by refreshToolboxSelection_() + * which does the actual refreshing. + */ +Blockly.Toolbox.prototype.refreshSelection = function() { + // this.showAll_(); +}; + +/** + * Move the toolbox to the edge. + */ +Blockly.Toolbox.prototype.position = function() { + const treeDiv = this.HtmlDiv; + if (!treeDiv) { + // Not initialized yet. + return; + } + const svg = this.workspace_.getParentSvg(); + const svgSize = Blockly.svgSize(svg); + if (this.horizontalLayout_) { + treeDiv.style.left = '0'; + treeDiv.style.height = 'auto'; + treeDiv.style.width = `${svgSize.width}px`; + this.height = treeDiv.offsetHeight; + if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { + // Top + treeDiv.style.top = '0'; + } else { + // Bottom + treeDiv.style.bottom = '0'; + } + } else { + if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { + // Right + treeDiv.style.right = '0'; + } else { + // Left + treeDiv.style.left = '0'; + } + treeDiv.style.height = '100%'; + } + this.flyout_.position(); +}; diff --git a/src/botPage/view/blockly/overrides/variables.js b/src/botPage/view/blockly/overrides/variables.js new file mode 100644 index 0000000000..8edf1dbca3 --- /dev/null +++ b/src/botPage/view/blockly/overrides/variables.js @@ -0,0 +1,32 @@ +/** + * Find all user-created variables that are in use in the workspace. + * For use by generators. + * To get a list of all variables on a workspace, including unused variables, + * call Workspace.getAllVariables. + * binary-bot: Required for JS generator to work. + * @param {!Blockly.Workspace} ws The workspace to search for variables. + * @return {!Array.} Array of variable models. + */ +Blockly.Variables.allUsedVarModels = function(ws) { + const blocks = ws.getAllBlocks(false); + const variableHash = Object.create(null); + // Iterate through every block and add each variable to the hash. + for (let i = 0; i < blocks.length; i++) { + const blockVariables = blocks[i].getVarModels(); + if (blockVariables) { + for (let j = 0; j < blockVariables.length; j++) { + const variable = blockVariables[j]; + var id = variable.getId(); + if (id) { + variableHash[id] = variable; + } + } + } + } + // Flatten the hash into a list. + const variableList = []; + for (var id in variableHash) { + variableList.push(variableHash[id]); + } + return variableList; +}; From 9208077333bc252c04953cf6ad4ec7dbf3856d3e Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:17:16 +0800 Subject: [PATCH 08/74] Customise blockly/index --- src/botPage/view/blockly/index.js | 52 +++++++++++++++++-------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index f464a78d63..b43163c2ab 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -1,5 +1,6 @@ -import './customBlockly'; -import blocks from './blocks'; +// Override Scratch +import './overrides'; +import './blocks'; import { isMainBlock, save, @@ -23,6 +24,7 @@ import { observer as globalObserver } from '../../../common/utils/observer'; import { showDialog } from '../../bot/tools'; const setBeforeUnload = off => { + return; if (off) { window.onbeforeunload = null; } else { @@ -172,6 +174,7 @@ const render = workspace => () => { const overrideBlocklyDefaultShape = () => { const addDownloadToMenu = block => { if (block instanceof Object) { + return; // eslint-disable-next-line no-param-reassign, max-len block.customContextMenu = function customContextMenu(options) { options.push({ @@ -206,14 +209,14 @@ const repaintDefaultColours = () => { Blockly.Msg.VARIABLES_DYNAMIC_HUE = '#DEDEDE'; Blockly.Msg.PROCEDURES_HUE = '#DEDEDE'; - Blockly.Blocks.logic.HUE = '#DEDEDE'; - Blockly.Blocks.loops.HUE = '#DEDEDE'; - Blockly.Blocks.math.HUE = '#DEDEDE'; - Blockly.Blocks.texts.HUE = '#DEDEDE'; - Blockly.Blocks.lists.HUE = '#DEDEDE'; - Blockly.Blocks.colour.HUE = '#DEDEDE'; - Blockly.Blocks.variables.HUE = '#DEDEDE'; - Blockly.Blocks.procedures.HUE = '#DEDEDE'; + // Blockly.Blocks.logic.HUE = '#DEDEDE'; + // Blockly.Blocks.loops.HUE = '#DEDEDE'; + // Blockly.Blocks.math.HUE = '#DEDEDE'; + // Blockly.Blocks.texts.HUE = '#DEDEDE'; + // Blockly.Blocks.lists.HUE = '#DEDEDE'; + // Blockly.Blocks.colour.HUE = '#DEDEDE'; + // Blockly.Blocks.variables.HUE = '#DEDEDE'; + // Blockly.Blocks.procedures.HUE = '#DEDEDE'; }; export default class _Blockly { @@ -221,17 +224,24 @@ export default class _Blockly { this.blocksXmlStr = ''; this.generatedJs = ''; // eslint-disable-next-line no-underscore-dangle - Blockly.WorkspaceSvg.prototype.preloadAudio_ = () => {}; // https://github.com/google/blockly/issues/299 + // Blockly.WorkspaceSvg.prototype.preloadAudio_ = () => {}; // https://github.com/google/blockly/issues/299 this.initPromise = new Promise(resolve => { $.get('xml/toolbox.xml', toolboxXml => { - blocks(); const workspace = Blockly.inject('blocklyDiv', { toolbox: xmlToStr(translateXml(toolboxXml.getElementsByTagName('xml')[0])), zoom : { - wheel: false, + wheel : true, + startScale: 1.1, }, - trashcan: false, + trashcan : true, + scrollbars: true, + media : 'image/scratch/', }); + + workspace.addChangeListener(event => { + $.notify(`Event: ${event.type} -- Name: ${event.name}`); + }); + const renderInstance = render(workspace); window.addEventListener('resize', renderInstance, false); renderInstance(); @@ -319,20 +329,20 @@ export default class _Blockly { save(filename, collection, xml); } run(limitations = {}) { - disableStrayBlocks(); + // disableStrayBlocks(); + + const workspaceCode = Blockly.JavaScript.workspaceToCode(Blockly.mainWorkspace); + let code; try { code = ` var BinaryBotPrivateInit, BinaryBotPrivateStart, BinaryBotPrivateBeforePurchase, BinaryBotPrivateDuringPurchase, BinaryBotPrivateAfterPurchase; - var BinaryBotPrivateLastTickTime var BinaryBotPrivateTickAnalysisList = []; - function BinaryBotPrivateRun(f, arg) { if (f) return f(arg); return false; } - function BinaryBotPrivateTickAnalysis() { var currentTickTime = Bot.getLastTick(true).epoch if (currentTickTime === BinaryBotPrivateLastTickTime) { @@ -343,13 +353,9 @@ function BinaryBotPrivateTickAnalysis() { BinaryBotPrivateRun(BinaryBotPrivateTickAnalysisList[BinaryBotPrivateI]); } } - var BinaryBotPrivateLimitations = ${JSON.stringify(limitations)}; - -${Blockly.JavaScript.workspaceToCode(Blockly.mainWorkspace)} - +${workspaceCode} BinaryBotPrivateRun(BinaryBotPrivateInit); - while(true) { BinaryBotPrivateTickAnalysis(); BinaryBotPrivateRun(BinaryBotPrivateStart) From c8094b35a8331099ea7c182693772df0de10095f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:18:11 +0800 Subject: [PATCH 09/74] Update setBlockTextColor --- src/botPage/view/blockly/utils.js | 42 +++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 11 deletions(-) diff --git a/src/botPage/view/blockly/utils.js b/src/botPage/view/blockly/utils.js index cacb8aa752..19ac53489c 100644 --- a/src/botPage/view/blockly/utils.js +++ b/src/botPage/view/blockly/utils.js @@ -148,18 +148,32 @@ export const deleteBlockIfExists = block => { export const setBlockTextColor = block => { Blockly.Events.recordUndo = false; - if (block.inputList instanceof Array) { - Array.from(block.inputList).forEach(inp => - inp.fieldRow.forEach(field => { - if (field instanceof Blockly.FieldLabel) { - const svgElement = field.getSvgRoot(); - if (svgElement) { - svgElement.style.setProperty('fill', 'white', 'important'); - } + + block.inputList.forEach(input => { + input.fieldRow.forEach(field => { + // Update FieldLabels + if (field instanceof Blockly.FieldLabel) { + const svgElement = field.getSvgRoot(); + if (svgElement) { + svgElement.style.setProperty('fill', 'white', 'important'); } - }) - ); - } + } + + // Update shadow block labels + if (field instanceof Blockly.FieldTextInput) { + const svgElement = field.getSvgRoot(); + if (svgElement) { + svgElement.childNodes.forEach(childNode => { + if (childNode.nodeName === 'text') { + childNode.style.setProperty('fill', 'white', 'important'); + } + }); + } + } + }); + }); + + // ??? const field = block.getField(); if (field) { const svgElement = field.getSvgRoot(); @@ -496,6 +510,12 @@ export const hideInteractionsFromBlockly = callback => { Blockly.Events.recordUndo = true; }; +export const hideEventsFromBlockly = callback => { + Blockly.Events.disable(); + callback(); + Blockly.Events.enable(); +}; + export const cleanBeforeExport = xml => { Array.from(xml.children).forEach(blockDom => { const blockId = blockDom.getAttribute('id'); From ba924909e57e36d712232420987e17a151786f8e Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:25:03 +0800 Subject: [PATCH 10/74] Emit event type only, restore original check_result --- .../blocks/after_purchase/check_result.js | 19 ++----------------- src/botPage/view/blockly/index.js | 2 +- 2 files changed, 3 insertions(+), 18 deletions(-) diff --git a/src/botPage/view/blockly/blocks/after_purchase/check_result.js b/src/botPage/view/blockly/blocks/after_purchase/check_result.js index 8c6a780c3c..9c2a8320e3 100644 --- a/src/botPage/view/blockly/blocks/after_purchase/check_result.js +++ b/src/botPage/view/blockly/blocks/after_purchase/check_result.js @@ -12,23 +12,8 @@ Blockly.Blocks.contract_check_result = { this.setTooltip(translate('True if the result matches the selection')); this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); }, - onchange(event) { - // insideAfterPurchase(this, ev, 'Check Result'); - - if (event.type === Blockly.Events.END_DRAG) { - // Ensure block is in `trade_definition` block - const parentBlock = this.getParent(); - while (parentBlock !== null) { - if (parentBlock.type === 'trade_definition') { - this.setDisabled(false); - return; - } - } - - $.notify('Woops, not in correct parent.'); - this.unplug(); - this.setDisabled(true); - } + onchange: function onchange(ev) { + insideAfterPurchase(this, ev, 'Check Result'); }, }; Blockly.JavaScript.contract_check_result = block => { diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index b43163c2ab..e27a5ce314 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -239,7 +239,7 @@ export default class _Blockly { }); workspace.addChangeListener(event => { - $.notify(`Event: ${event.type} -- Name: ${event.name}`); + $.notify(`Event: ${event.type}`); }); const renderInstance = render(workspace); From 2190a092f5659eae3aa98ca7db6630c4f06e9a1c Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:25:16 +0800 Subject: [PATCH 11/74] Update package-lock.json --- package-lock.json | 400 +++++++++++++++++++++++++++++++++++++++------- 1 file changed, 340 insertions(+), 60 deletions(-) diff --git a/package-lock.json b/package-lock.json index a47d191ce6..f905f35af6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -3177,7 +3177,8 @@ "abab": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.0.tgz", - "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==" + "integrity": "sha512-sY5AXXVZv4Y1VACTtR11UJCPHHudgY5i26Qj5TypE6DKlIApbwb5uqhXcJ5UUGbvZNRh7EeIoW+LrJumBsKp7w==", + "dev": true }, "abbrev": { "version": "1.1.1", @@ -3198,7 +3199,8 @@ "acorn": { "version": "5.7.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz", - "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==" + "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==", + "dev": true }, "acorn-dynamic-import": { "version": "2.0.2", @@ -3221,6 +3223,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.0.tgz", "integrity": "sha512-hMtHj3s5RnuhvHPowpBYvJVj3rAar82JiDQHvGs1zO0l10ocX/xEdBShNHTJaboucJUsScghp74pH3s7EnHHQw==", + "dev": true, "requires": { "acorn": "^6.0.1", "acorn-walk": "^6.0.1" @@ -3229,7 +3232,8 @@ "acorn": { "version": "6.0.5", "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.0.5.tgz", - "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==" + "integrity": "sha512-i33Zgp3XWtmZBMNvCr4azvOFeWVw1Rk6p3hfi3LUDvIFraOMywb1kAtrbi+med14m4Xfpqm3zRZMT+c0FNE7kg==", + "dev": true } } }, @@ -3253,7 +3257,8 @@ "acorn-walk": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.1.1.tgz", - "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==" + "integrity": "sha512-OtUw6JUTgxA2QoqqmrmQ7F2NYqiBPi/L2jqHyFtllhOUvXYQXf0Z1CYUinIfyT4bTCGmrA7gX9FvHA81uzCoVw==", + "dev": true }, "add-px-to-style": { "version": "1.0.0", @@ -3264,6 +3269,7 @@ "version": "6.7.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.7.0.tgz", "integrity": "sha512-RZXPviBTtfmtka9n9sy1N5M5b82CbxWIR6HIis4s3WQTXDJamc/0gpCWNGz6EWdWp4DOfjzJfhz/AS9zVPjjWg==", + "dev": true, "requires": { "fast-deep-equal": "^2.0.1", "fast-json-stable-stringify": "^2.0.0", @@ -3500,7 +3506,8 @@ "array-equal": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz", - "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=" + "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=", + "dev": true }, "array-find-index": { "version": "1.0.2", @@ -3607,6 +3614,7 @@ "version": "0.2.4", "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "dev": true, "requires": { "safer-buffer": "~2.1.0" } @@ -3651,7 +3659,8 @@ "assert-plus": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", - "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=", + "dev": true }, "assertion-error": { "version": "1.1.0", @@ -3718,7 +3727,8 @@ "async-limiter": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.0.tgz", - "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==" + "integrity": "sha512-jp/uFnooOiO+L211eZOoSyzpOITMXx1rBITauYykG3BRYPu8h0UcxsPNB04RR5vo4Tyz3+ay17tR6JVf9qzYWg==", + "dev": true }, "async-settle": { "version": "1.0.0", @@ -3732,7 +3742,8 @@ "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=", + "dev": true }, "atob": { "version": "2.1.2", @@ -3743,12 +3754,14 @@ "aws-sign2": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", - "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=", + "dev": true }, "aws4": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", - "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==", + "dev": true }, "axobject-query": { "version": "2.0.2", @@ -4257,6 +4270,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "dev": true, "requires": { "tweetnacl": "^0.14.3" } @@ -4316,10 +4330,70 @@ } }, "blockly": { - "version": "github:google/blockly#59e5ac6897d2d9800e0010b43681c9f7743e2ceb", - "from": "github:google/blockly#59e5ac6", + "version": "github:google/blockly#9c4f9d6c1693c3c25160f008d3eaa2539127c9e8", + "from": "github:google/blockly#9c4f9d6", + "dev": true, "requires": { - "jsdom": "^11.11.0" + "jsdom": "^13.2.0" + }, + "dependencies": { + "acorn": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.1.1.tgz", + "integrity": "sha512-jPTiwtOxaHNaAPg/dmrJ/beuzLRnXtB0kQPQ8JpotKJgTB6rX6c8mlf315941pyjBSaPg8NHXS9fhP4u17DpGA==", + "dev": true + }, + "jsdom": { + "version": "13.2.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-13.2.0.tgz", + "integrity": "sha512-cG1NtMWO9hWpqRNRR3dSvEQa8bFI6iLlqU2x4kwX51FQjp0qus8T9aBaAO6iGp3DeBrhdwuKxckknohkmfvsFw==", + "dev": true, + "requires": { + "abab": "^2.0.0", + "acorn": "^6.0.4", + "acorn-globals": "^4.3.0", + "array-equal": "^1.0.0", + "cssom": "^0.3.4", + "cssstyle": "^1.1.1", + "data-urls": "^1.1.0", + "domexception": "^1.0.1", + "escodegen": "^1.11.0", + "html-encoding-sniffer": "^1.0.2", + "nwsapi": "^2.0.9", + "parse5": "5.1.0", + "pn": "^1.1.0", + "request": "^2.88.0", + "request-promise-native": "^1.0.5", + "saxes": "^3.1.5", + "symbol-tree": "^3.2.2", + "tough-cookie": "^2.5.0", + "w3c-hr-time": "^1.0.1", + "w3c-xmlserializer": "^1.0.1", + "webidl-conversions": "^4.0.2", + "whatwg-encoding": "^1.0.5", + "whatwg-mimetype": "^2.3.0", + "whatwg-url": "^7.0.0", + "ws": "^6.1.2", + "xml-name-validator": "^3.0.0" + } + }, + "parse5": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.0.tgz", + "integrity": "sha512-fxNG2sQjHvlVAYmzBZS9YlDp6PTSSDwa98vkD4QgVDDCAo84z5X1t5XyJQ62ImdLXx5NdIIfihey6xpum9/gRQ==", + "dev": true + }, + "whatwg-url": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", + "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, + "requires": { + "lodash.sortby": "^4.7.0", + "tr46": "^1.0.1", + "webidl-conversions": "^4.0.2" + } + } } }, "bn.js": { @@ -4375,7 +4449,8 @@ "browser-process-hrtime": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-0.1.3.tgz", - "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==" + "integrity": "sha512-bRFnI4NnjO6cnyLmOV/7PVoDEMJChlcfN0z4s1YMBY989/SvlfMI1lgCnkFUs53e9gQF+w7qu7XdllSTiSl8Aw==", + "dev": true }, "browser-resolve": { "version": "1.11.3", @@ -4594,7 +4669,8 @@ "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", - "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=", + "dev": true }, "center-align": { "version": "0.1.3", @@ -4967,6 +5043,7 @@ "version": "1.0.7", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "dev": true, "requires": { "delayed-stream": "~1.0.0" } @@ -5292,12 +5369,14 @@ "cssom": { "version": "0.3.4", "resolved": "https://registry.npmjs.org/cssom/-/cssom-0.3.4.tgz", - "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==" + "integrity": "sha512-+7prCSORpXNeR4/fUP3rL+TzqtiFfhMvTd7uEqMdgPvLPt4+uzFUeufx5RHjGTACCargg/DiEt/moMQmvnfkog==", + "dev": true }, "cssstyle": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-1.1.1.tgz", "integrity": "sha512-364AI1l/M5TYcFH83JnOH/pSqgaNnKmYgKrm0didZMGKWjQB60dymwWy1rKUgL3J1ffdq9xVi2yGLHdSjjSNog==", + "dev": true, "requires": { "cssom": "0.3.x" } @@ -5330,6 +5409,7 @@ "version": "1.14.1", "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -5338,6 +5418,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz", "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==", + "dev": true, "requires": { "abab": "^2.0.0", "whatwg-mimetype": "^2.2.0", @@ -5348,6 +5429,7 @@ "version": "7.0.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-7.0.0.tgz", "integrity": "sha512-37GeVSIJ3kn1JgKyjiYNmSLP1yzbpb29jdmwBSgkD9h40/hyrR/OifpVUndji3tmwGgD8qpw7iQu3RSbCrBpsQ==", + "dev": true, "requires": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", @@ -5447,7 +5529,8 @@ "deep-is": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=" + "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "dev": true }, "default-compare": { "version": "1.0.0", @@ -5576,7 +5659,8 @@ "delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=", + "dev": true }, "delegates": { "version": "1.0.0", @@ -5719,6 +5803,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz", "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==", + "dev": true, "requires": { "webidl-conversions": "^4.0.2" } @@ -5790,6 +5875,7 @@ "version": "0.1.2", "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "dev": true, "requires": { "jsbn": "~0.1.0", "safer-buffer": "^2.1.0" @@ -6204,6 +6290,7 @@ "version": "1.11.0", "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.11.0.tgz", "integrity": "sha512-IeMV45ReixHS53K/OmfKAIztN/igDHzTJUhZM3k1jMhIZWjk45SMwAtBsEXiJp3vSPmTcu6CXn7mDvFHRN66fw==", + "dev": true, "requires": { "esprima": "^3.1.3", "estraverse": "^4.2.0", @@ -6595,7 +6682,8 @@ "esprima": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/esprima/-/esprima-3.1.3.tgz", - "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=" + "integrity": "sha1-/cpRzuYTOJXjyI1TXOSdv/YqRjM=", + "dev": true }, "esquery": { "version": "1.0.1", @@ -6618,12 +6706,14 @@ "estraverse": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.2.0.tgz", - "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=" + "integrity": "sha1-De4/7TH81GlhjOc0IJn8GvoL2xM=", + "dev": true }, "esutils": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz", - "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=" + "integrity": "sha1-Cr9PHKpbyx96nYrMbepPqqBLrJs=", + "dev": true }, "etag": { "version": "1.8.1", @@ -6776,10 +6866,50 @@ } } }, + "exports-loader": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/exports-loader/-/exports-loader-0.6.3.tgz", + "integrity": "sha1-V9x4kX9wm5byR/qR5ptVTIVQE8g=", + "dev": true, + "requires": { + "loader-utils": "0.2.x", + "source-map": "0.1.x" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", + "dev": true }, "extend-shallow": { "version": "2.0.1", @@ -6821,7 +6951,8 @@ "extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", - "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=", + "dev": true }, "fancy-log": { "version": "1.3.3", @@ -6838,7 +6969,8 @@ "fast-deep-equal": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz", - "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=" + "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk=", + "dev": true }, "fast-glob": { "version": "2.2.6", @@ -7163,12 +7295,14 @@ "fast-json-stable-stringify": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", - "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=", + "dev": true }, "fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=" + "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", + "dev": true }, "faye-websocket": { "version": "0.10.0", @@ -7768,7 +7902,8 @@ "forever-agent": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", - "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=", + "dev": true }, "fork-stream": { "version": "0.0.4", @@ -7780,6 +7915,7 @@ "version": "2.3.3", "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.6", @@ -8565,6 +8701,7 @@ "version": "0.1.7", "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "dev": true, "requires": { "assert-plus": "^1.0.0" } @@ -9846,12 +9983,14 @@ "har-schema": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", - "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=", + "dev": true }, "har-validator": { "version": "5.1.3", "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz", "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==", + "dev": true, "requires": { "ajv": "^6.5.5", "har-schema": "^2.0.0" @@ -10030,6 +10169,7 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz", "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==", + "dev": true, "requires": { "whatwg-encoding": "^1.0.1" } @@ -10064,6 +10204,7 @@ "version": "1.2.0", "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "jsprim": "^1.2.2", @@ -10167,6 +10308,7 @@ "version": "0.4.24", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dev": true, "requires": { "safer-buffer": ">= 2.1.2 < 3" } @@ -10259,6 +10401,45 @@ "integrity": "sha1-pVxS5McFx2XKIQ6SQqBrvMiqf2Y=", "dev": true }, + "imports-loader": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/imports-loader/-/imports-loader-0.6.5.tgz", + "integrity": "sha1-rnRlMDHVnjezwvslRKxhrq41MKY=", + "dev": true, + "requires": { + "loader-utils": "0.2.x", + "source-map": "0.1.x" + }, + "dependencies": { + "big.js": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", + "integrity": "sha512-+hN/Zh2D08Mx65pZ/4g5bsmNiZUuChDiQfTUQ7qJr4/kuopCr88xZsAXv6mBoZEsUI4OuGHlX59qE94K2mMW8Q==", + "dev": true + }, + "loader-utils": { + "version": "0.2.17", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-0.2.17.tgz", + "integrity": "sha1-+G5jdNQyBabmxg6RlvF8Apm/s0g=", + "dev": true, + "requires": { + "big.js": "^3.1.3", + "emojis-list": "^2.0.0", + "json5": "^0.5.0", + "object-assign": "^4.0.1" + } + }, + "source-map": { + "version": "0.1.43", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.1.43.tgz", + "integrity": "sha1-wkvBRspRfBRx9drL4lcbK3+eM0Y=", + "dev": true, + "requires": { + "amdefine": ">=0.0.4" + } + } + } + }, "imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", @@ -10710,7 +10891,8 @@ "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", - "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=", + "dev": true }, "is-unc-path": { "version": "1.0.0", @@ -10768,7 +10950,8 @@ "isstream": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", - "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=", + "dev": true }, "istanbul-api": { "version": "2.1.6", @@ -13826,9 +14009,9 @@ } }, "jquery": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.0.tgz", - "integrity": "sha512-ggRCXln9zEqv6OqAGXFEcshF5dSBvCkzj6Gm2gzuR5fWawaX8t7cxKVkkygKODrDAzKdoYw3l/e3pm3vlT4IbQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", + "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=", "dev": true }, "jquery-mousewheel": { @@ -13909,12 +14092,14 @@ "jsbn": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", - "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "dev": true }, "jsdom": { "version": "11.12.0", "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-11.12.0.tgz", "integrity": "sha512-y8Px43oyiBM13Zc1z780FrfNLJCXTL40EWlty/LXUtcjykRBNgLlCjWXpfSPBl2iv+N7koQN+dvqszHZgT/Fjw==", + "dev": true, "requires": { "abab": "^2.0.0", "acorn": "^5.5.3", @@ -13948,6 +14133,7 @@ "version": "5.2.2", "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "dev": true, "requires": { "async-limiter": "~1.0.0" } @@ -13975,12 +14161,14 @@ "json-schema": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", - "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=", + "dev": true }, "json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", + "dev": true }, "json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -13991,7 +14179,8 @@ "json-stringify-safe": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", - "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=", + "dev": true }, "json2csv": { "version": "3.11.5", @@ -14046,6 +14235,7 @@ "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "dev": true, "requires": { "assert-plus": "1.0.0", "extsprintf": "1.3.0", @@ -14135,7 +14325,8 @@ "left-pad": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/left-pad/-/left-pad-1.3.0.tgz", - "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==" + "integrity": "sha512-XI5MPzVNApjAyhQzphX8BkmKsKUxD4LdyK24iZeQGinBN9yTQT3bFlCBy/aVx2HrNcqQGsdot8ghrjyrvMCoEA==", + "dev": true }, "leven": { "version": "2.1.0", @@ -14147,6 +14338,7 @@ "version": "0.3.0", "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", "integrity": "sha1-OwmSTt+fCDwEkP3UwLxEIeBHZO4=", + "dev": true, "requires": { "prelude-ls": "~1.1.2", "type-check": "~0.3.2" @@ -14748,7 +14940,8 @@ "lodash.sortby": { "version": "4.7.0", "resolved": "https://registry.npmjs.org/lodash.sortby/-/lodash.sortby-4.7.0.tgz", - "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=" + "integrity": "sha1-7dFMgk4sycHgsKG0K7UhBRakJDg=", + "dev": true }, "lodash.template": { "version": "3.6.2", @@ -15529,12 +15722,14 @@ "mime-db": { "version": "1.37.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.37.0.tgz", - "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==" + "integrity": "sha512-R3C4db6bgQhlIhPU48fUtdVmKnflq+hRdad7IyKhtFj06VPNVdk2RhiYL3UjQIlso8L+YxAtFkobT0VK+S/ybg==", + "dev": true }, "mime-types": { "version": "2.1.21", "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.21.tgz", "integrity": "sha512-3iL6DbwpyLzjR3xHSFNFeb9Nz/M8WDkX33t1GFQnFOllWk8pOrh/LSrB5OXlnlW5P9LH73X6loW/eogc+F5lJg==", + "dev": true, "requires": { "mime-db": "~1.37.0" } @@ -16075,12 +16270,14 @@ "nwsapi": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.0.9.tgz", - "integrity": "sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==" + "integrity": "sha512-nlWFSCTYQcHk/6A9FFnfhKc14c3aFhfdNBXgo8Qgi9QTBu/qg3Ww+Uiz9wMzXd1T8GFxPc2QIHB6Qtf2XFryFQ==", + "dev": true }, "oauth-sign": { "version": "0.9.0", "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", - "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==", + "dev": true }, "object-assign": { "version": "4.1.1", @@ -16327,6 +16524,7 @@ "version": "0.8.2", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.2.tgz", "integrity": "sha1-NkxeQJ0/TWMB1sC0wFu6UBgK62Q=", + "dev": true, "requires": { "deep-is": "~0.1.3", "fast-levenshtein": "~2.0.4", @@ -16578,7 +16776,8 @@ "parse5": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/parse5/-/parse5-4.0.0.tgz", - "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==" + "integrity": "sha512-VrZ7eOd3T1Fk4XWNXMgiGBK/z0MG48BWG2uQNU4I72fkQuKUTZpl+u9k+CxEG0twMVzSmXEEz12z5Fnw1jIQFA==", + "dev": true }, "parseurl": { "version": "1.3.2", @@ -16804,7 +17003,8 @@ "pn": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/pn/-/pn-1.1.0.tgz", - "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==" + "integrity": "sha512-2qHaIQr2VLRFoxe2nASzsV6ef4yOOH+Fi9FBOVH6cqeSgUnoyySPZkxzLuzd+RYOQTRpROA0ztTMqxROKSb/nA==", + "dev": true }, "posix-character-classes": { "version": "0.1.1", @@ -16826,7 +17026,8 @@ "prelude-ls": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=" + "integrity": "sha1-IZMqVJ9eUv/ZqCf1cOBL5iqX2lQ=", + "dev": true }, "prepend-http": { "version": "1.0.4", @@ -17150,7 +17351,8 @@ "psl": { "version": "1.1.31", "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.31.tgz", - "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==" + "integrity": "sha512-/6pt4+C+T+wZUieKR620OpzN/LlnNKuWjy1iFLQ/UG35JqHlR/89MP1d96dUfkf6Dne3TuLQzOYEYshJ+Hx8mw==", + "dev": true }, "public-encrypt": { "version": "4.0.3", @@ -17190,12 +17392,14 @@ "punycode": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", + "dev": true }, "qs": { "version": "6.5.2", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", - "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==", + "dev": true }, "query-string": { "version": "4.3.4", @@ -17951,6 +18155,7 @@ "version": "2.88.0", "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "dev": true, "requires": { "aws-sign2": "~0.7.0", "aws4": "^1.8.0", @@ -17977,12 +18182,14 @@ "punycode": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", - "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=", + "dev": true }, "tough-cookie": { "version": "2.4.3", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "dev": true, "requires": { "psl": "^1.1.24", "punycode": "^1.4.1" @@ -17994,6 +18201,7 @@ "version": "1.1.1", "resolved": "https://registry.npmjs.org/request-promise-core/-/request-promise-core-1.1.1.tgz", "integrity": "sha1-Pu4AssWqgyOc+wTFcA2jb4HNCLY=", + "dev": true, "requires": { "lodash": "^4.13.1" } @@ -18002,6 +18210,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/request-promise-native/-/request-promise-native-1.0.5.tgz", "integrity": "sha1-UoF3D2jgyXGeUWP9P6tIIhX0/aU=", + "dev": true, "requires": { "request-promise-core": "1.1.1", "stealthy-require": "^1.1.0", @@ -18297,6 +18506,11 @@ } } }, + "rxjs-compat": { + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/rxjs-compat/-/rxjs-compat-6.5.1.tgz", + "integrity": "sha512-k0aaMAYFg+DCIUxo3yJcUamFRenWLR23e/VJCQvKOxwSd9DQlwcSn2ov+7b23rKaX4weKG7OrzEBWGqQPkQ07A==" + }, "safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -18320,7 +18534,8 @@ "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true }, "sane": { "version": "4.1.0", @@ -18708,7 +18923,17 @@ "sax": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", - "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==" + "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", + "dev": true + }, + "saxes": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/saxes/-/saxes-3.1.9.tgz", + "integrity": "sha512-FZeKhJglhJHk7eWG5YM0z46VHmI3KJpMBAQm3xa9meDvd+wevB5GuBB0wc0exPInZiBBHqi00DbS8AcvCGCFMw==", + "dev": true, + "requires": { + "xmlchars": "^1.3.1" + } }, "scheduler": { "version": "0.12.0", @@ -18719,6 +18944,16 @@ "object-assign": "^4.1.1" } }, + "scratch-blocks": { + "version": "0.1.0-prerelease.1556115881", + "resolved": "https://registry.npmjs.org/scratch-blocks/-/scratch-blocks-0.1.0-prerelease.1556115881.tgz", + "integrity": "sha512-BpMvhUr5yrM1RIELLcus5tYpGOi8tSDlf5OD5XcaqWBQxU7Rgeep7eDEtIkoYhs30gjwJfdjc1qIwlxow0Pu7Q==", + "dev": true, + "requires": { + "exports-loader": "0.6.3", + "imports-loader": "0.6.5" + } + }, "scroll": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/scroll/-/scroll-2.0.3.tgz", @@ -19050,7 +19285,8 @@ "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "source-map-resolve": { "version": "0.5.2", @@ -19168,6 +19404,7 @@ "version": "1.16.1", "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "dev": true, "requires": { "asn1": "~0.2.3", "assert-plus": "^1.0.0", @@ -19237,7 +19474,8 @@ "stealthy-require": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/stealthy-require/-/stealthy-require-1.1.1.tgz", - "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=" + "integrity": "sha1-NbCYdbT/SfJqd35QmzCQoyJr8ks=", + "dev": true }, "stream-browserify": { "version": "2.0.2", @@ -19507,7 +19745,8 @@ "symbol-tree": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.2.tgz", - "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=" + "integrity": "sha1-rifbOPZgp64uHDt9G8KQgZuFGeY=", + "dev": true }, "table": { "version": "4.0.2", @@ -19955,6 +20194,7 @@ "version": "2.5.0", "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "dev": true, "requires": { "psl": "^1.1.28", "punycode": "^2.1.1" @@ -19964,6 +20204,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/tr46/-/tr46-1.0.1.tgz", "integrity": "sha1-qLE/1r/SSJUZZ0zN5VujaTtwbQk=", + "dev": true, "requires": { "punycode": "^2.1.0" } @@ -20023,6 +20264,7 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "dev": true, "requires": { "safe-buffer": "^5.0.1" } @@ -20030,12 +20272,14 @@ "tweetnacl": { "version": "0.14.5", "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", - "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "dev": true }, "type-check": { "version": "0.3.2", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", "integrity": "sha1-WITKtRLPHTVeP7eE8wgEsrUg23I=", + "dev": true, "requires": { "prelude-ls": "~1.1.2" } @@ -20394,6 +20638,7 @@ "version": "4.2.2", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.2.2.tgz", "integrity": "sha512-KY9Frmirql91X2Qgjry0Wd4Y+YTdrdZheS8TFwvkbLWf/G5KNJDCh6pKL5OZctEW4+0Baa5idK2ZQuELRwPznQ==", + "dev": true, "requires": { "punycode": "^2.1.0" } @@ -20475,7 +20720,8 @@ "uuid": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", - "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==", + "dev": true }, "vali-date": { "version": "1.0.0", @@ -20503,6 +20749,7 @@ "version": "1.10.0", "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "dev": true, "requires": { "assert-plus": "^1.0.0", "core-util-is": "1.0.2", @@ -20756,10 +21003,22 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/w3c-hr-time/-/w3c-hr-time-1.0.1.tgz", "integrity": "sha1-gqwr/2PZUOqeMYmlimViX+3xkEU=", + "dev": true, "requires": { "browser-process-hrtime": "^0.1.2" } }, + "w3c-xmlserializer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-1.1.2.tgz", + "integrity": "sha512-p10l/ayESzrBMYWRID6xbuCKh2Fp77+sA0doRuGn4tTIMrrZVeqfpKjXHY+oDh3K4nLdPgNwMTVP6Vp4pvqbNg==", + "dev": true, + "requires": { + "domexception": "^1.0.1", + "webidl-conversions": "^4.0.2", + "xml-name-validator": "^3.0.0" + } + }, "walker": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.7.tgz", @@ -21120,7 +21379,8 @@ "webidl-conversions": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-4.0.2.tgz", - "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==" + "integrity": "sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==", + "dev": true }, "webpack": { "version": "3.12.0", @@ -21396,6 +21656,7 @@ "version": "1.0.5", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-1.0.5.tgz", "integrity": "sha512-b5lim54JOPN9HtzvK9HFXvBma/rnfFeqsic0hSpjtDbVxR3dJKLc+KB4V6GgiGOvl7CY/KNh8rxSo9DKQrnUEw==", + "dev": true, "requires": { "iconv-lite": "0.4.24" } @@ -21403,12 +21664,14 @@ "whatwg-mimetype": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-2.3.0.tgz", - "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==" + "integrity": "sha512-M4yMwr6mAnQz76TbJm914+gPpB/nCwvZbJU28cUD6dR004SAxDLOOSUaB1JDRqLtaOV/vi0IC5lEAGFgrjGv/g==", + "dev": true }, "whatwg-url": { "version": "6.5.0", "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-6.5.0.tgz", "integrity": "sha512-rhRZRqx/TLJQWUpQ6bmrt2UV4f0HCQ463yQuONJqC6fO2VoEb1pTYddbe59SkYq87aoM5A3bdhMZiUiVws+fzQ==", + "dev": true, "requires": { "lodash.sortby": "^4.7.0", "tr46": "^1.0.1", @@ -21464,7 +21727,8 @@ "wordwrap": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", - "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=" + "integrity": "sha1-J1hIEIkUVqQXHI0CJkQa3pDLyus=", + "dev": true }, "wrap-ansi": { "version": "2.1.0", @@ -21523,10 +21787,26 @@ "signal-exit": "^3.0.2" } }, + "ws": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-6.2.1.tgz", + "integrity": "sha512-GIyAXC2cB7LjvpgMt9EKS2ldqr0MTrORaleiOno6TweZ6r3TKtoFQWay/2PceJ3RuBasOHzXNn5Lrw1X0bEjqA==", + "dev": true, + "requires": { + "async-limiter": "~1.0.0" + } + }, "xml-name-validator": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-3.0.0.tgz", - "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==" + "integrity": "sha512-A5CUptxDsvxKJEU3yO6DuWBSJz/qizqzJKOMIfUJHETbBw/sFaDxgd6fxm1ewUaM0jZ444Fc5vC5ROYurg/4Pw==", + "dev": true + }, + "xmlchars": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/xmlchars/-/xmlchars-1.3.1.tgz", + "integrity": "sha512-tGkGJkN8XqCod7OT+EvGYK5Z4SfDQGD30zAa58OcnAa0RRWgzUEK72tkXhsX1FZd+rgnhRxFtmO+ihkp8LHSkw==", + "dev": true }, "xtend": { "version": "4.0.1", From ed5100104acf9b80e928708555f87b7251aa12b8 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 16:28:19 +0800 Subject: [PATCH 12/74] Shadow blocks for restart_* blocks --- static/xml/toolbox.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/xml/toolbox.xml b/static/xml/toolbox.xml index fc8ef38db6..71c7e0b491 100644 --- a/static/xml/toolbox.xml +++ b/static/xml/toolbox.xml @@ -296,16 +296,16 @@ - + FALSE - + - + FALSE - + From 88eeeba1e9ac133c1e7a1b78c9bf2bd1b0d5338f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Mon, 6 May 2019 17:04:10 +0800 Subject: [PATCH 13/74] Shadow blocks for restart_* blocks --- static/xml/main.xml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/static/xml/main.xml b/static/xml/main.xml index 62d1385104..6fa457e6c4 100644 --- a/static/xml/main.xml +++ b/static/xml/main.xml @@ -19,16 +19,16 @@ - + FALSE - + - + FALSE - + From 39bb073150a49232a606dadcef1031745e6e278f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 11:53:48 +0800 Subject: [PATCH 14/74] Block updates --- .../Functions/procedures_callnoreturn.js | 47 +++--- .../Functions/procedures_callreturn.js | 26 ++-- .../Functions/procedures_defnoreturn.js | 136 ++++++++---------- .../Functions/procedures_defreturn.js | 27 ++-- .../Advanced/Functions/procedures_ifreturn.js | 18 +-- .../Advanced/List/lists_create_with.js | 1 + .../Scratch/Advanced/List/lists_getIndex.js | 17 ++- .../Scratch/Advanced/List/lists_getSublist.js | 19 +-- .../Scratch/Advanced/List/lists_repeat.js | 3 + .../Scratch/Advanced/List/lists_setIndex.js | 11 +- .../Scratch/Advanced/List/lists_sort.js | 2 + .../Scratch/Advanced/List/lists_split.js | 8 +- .../Scratch/Advanced/List/lists_statement.js | 2 - .../Loops/controls_flow_statements.js | 8 +- .../Scratch/Advanced/Loops/controls_for.js | 4 + .../Advanced/Loops/controls_forEach.js | 3 + .../Advanced/Loops/controls_repeat_ext.js | 2 + .../Advanced/Variable/variables_get.js | 1 + .../Advanced/Variable/variables_set.js | 1 + .../Binary/After Purchase/check_result.js | 1 - .../Binary/After Purchase/read_details.js | 1 - .../Binary/Before Purchase/ask_price.js | 1 - .../Binary/Before Purchase/purchase.js | 1 - .../Binary/During Purchase/check_sell.js | 1 - .../Binary/During Purchase/sell_at_market.js | 1 - .../Scratch/Binary/Indicators/bb_statement.js | 1 + .../Binary/Indicators/bba_statement.js | 1 + .../Binary/Indicators/ema_statement.js | 1 + .../Binary/Indicators/emaa_statement.js | 1 + .../Binary/Indicators/macda_statement.js | 1 + .../Binary/Indicators/rsi_statement.js | 1 + .../Binary/Indicators/rsia_statement.js | 1 + .../Binary/Indicators/sma_statement.js | 1 + .../Binary/Indicators/smaa_statement.js | 1 + .../Scratch/Binary/Tools/Misc./loader.js | 1 + .../Trade Definition/trade_definition.js | 9 +- .../trade_definition_contracttype.js | 3 - .../trade_definition_market.js | 3 - .../trade_definition_restartbuysell.js | 3 - .../trade_definition_restartonerror.js | 3 - .../trade_definition_tradeoptions.js | 39 ++--- .../trade_definition_tradetype.js | 3 - .../blocks/Scratch/Logic/controls_if.js | 69 ++++----- .../blocks/Scratch/Math/math_change.js | 1 + .../Scratch/Math/math_number_property.js | 2 + .../blocks/Scratch/Math/math_on_list.js | 1 + .../blocks/Scratch/Math/math_random_int.js | 2 + .../view/blockly/blocks/Scratch/Text/text.js | 1 + .../blocks/Scratch/Text/text_append.js | 30 ++-- .../blocks/Scratch/Text/text_changeCase.js | 2 + .../blocks/Scratch/Text/text_charAt.js | 18 +-- .../blocks/Scratch/Text/text_getSubstring.js | 28 ++-- .../blockly/blocks/Scratch/Text/text_join.js | 1 + .../blocks/Scratch/Text/text_prompt_ext.js | 1 + .../blocks/Scratch/Text/text_statement.js | 4 +- .../blocks/before_purchase/purchase.js | 2 +- src/botPage/view/blockly/blocks/index.js | 9 +- 57 files changed, 294 insertions(+), 292 deletions(-) diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callnoreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callnoreturn.js index 04219894ef..1b9ac008f3 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callnoreturn.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callnoreturn.js @@ -3,9 +3,9 @@ import { translate } from '../../../../../../../common/i18n'; Blockly.Blocks.procedures_callnoreturn = { init() { - this.arguments_ = []; - this.argumentVarModels_ = []; - this.previousDisabledState_ = false; + this.arguments = []; + this.argumentVarModels = []; + this.previousDisabledState = false; this.jsonInit({ message0: '%1 %2', @@ -46,12 +46,12 @@ Blockly.Blocks.procedures_callnoreturn = { return; } - if (event.type == Blockly.Events.BLOCK_CREATE && event.ids.indexOf(this.id) !== -1) { + if (event.type === Blockly.Events.BLOCK_CREATE && event.ids.indexOf(this.id) !== -1) { // Look for the case where a procedure call was created (usually through // paste) and there is no matching definition. In this case, create // an empty definition block with the correct signature. const name = this.getProcedureCall(); - const def = this.getProcedureDefinition(name); + let def = this.getProcedureDefinition(name); // Set data of `this` block to 'procedure definition'-block `id` so we can keep track of their relation. if (!def) { @@ -63,7 +63,7 @@ Blockly.Blocks.procedures_callnoreturn = { if ( def && - (def.type !== this.defType_ || JSON.stringify(def.arguments_) !== JSON.stringify(this.arguments_)) + (def.type !== this.defType || JSON.stringify(def.arguments) !== JSON.stringify(this.arguments)) ) { // The signatures don't match. def = null; @@ -83,7 +83,7 @@ Blockly.Blocks.procedures_callnoreturn = { */ const xml = document.createElement('xml'); const block = document.createElement('block'); - block.setAttribute('type', this.defType_); + block.setAttribute('type', this.defType); const xy = this.getRelativeToSurfaceXY(); const x = xy.x + Blockly.SNAP_RADIUS * (this.RTL ? -1 : 1); @@ -130,16 +130,17 @@ Blockly.Blocks.procedures_callnoreturn = { // This should only be possible programatically and may indicate a problem // with event grouping. If you see this message please investigate. If the // use ends up being valid we may need to reorder events in the undo stack. + // eslint-disable-next-line no-console console.log('Saw an existing group while responding to a definition change'); } Blockly.Events.setGroup(event.group); if (event.newValue) { - this.previousDisabledState_ = this.disabled; + this.previousDisabledState = this.disabled; this.setDisabled(true); } else { - this.setDisabled(this.previousDisabledState_); + this.setDisabled(this.previousDisabledState); } Blockly.Events.setGroup(oldGroup); @@ -188,24 +189,24 @@ Blockly.Blocks.procedures_callnoreturn = { * @private * @this Blockly.Block */ - setProcedureParameters_(paramNames) { + setProcedureParameters(paramNames) { // Rebuild the block's arguments. - this.arguments_ = [].concat(paramNames); + this.arguments = [].concat(paramNames); // And rebuild the argument model list. - this.argumentVarModels_ = this.arguments_.map(argumentName => + this.argumentVarModels = this.arguments.map(argumentName => Blockly.Variables.getOrCreateVariablePackage(this.workspace, null, argumentName, '') ); - this.updateShape_(); + this.updateShape(); }, /** * Modify this block to have the correct number of arguments. * @private * @this Blockly.Block */ - updateShape_() { - this.arguments_.forEach((argumentName, i) => { + updateShape() { + this.arguments.forEach((argumentName, i) => { let field = this.getField(`ARGNAME${i}`); if (field) { // Ensure argument name is up to date. @@ -226,6 +227,7 @@ Blockly.Blocks.procedures_callnoreturn = { }); // Remove deleted inputs. + let i = this.arguments.length; while (this.getInput(`ARG${i}`)) { this.removeInput(`ARG${i}`); i++; @@ -235,7 +237,7 @@ Blockly.Blocks.procedures_callnoreturn = { const topRow = this.getInput('TOPROW'); if (topRow) { - if (this.arguments_.length) { + if (this.arguments.length) { if (!this.getField('WITH')) { topRow.appendField(translate('with:'), 'WITH'); topRow.init(); @@ -254,7 +256,7 @@ Blockly.Blocks.procedures_callnoreturn = { const container = document.createElement('mutation'); container.setAttribute('name', this.getProcedureCall()); - this.arguments_.forEach(argumentName => { + this.arguments.forEach(argumentName => { const parameter = document.createElement('arg'); parameter.setAttribute('name', argumentName); container.appendChild(parameter); @@ -281,7 +283,7 @@ Blockly.Blocks.procedures_callnoreturn = { } }); - this.setProcedureParameters_(args, paramIds); + this.setProcedureParameters(args, paramIds); }, /** * Return all variables referenced by this block. @@ -289,7 +291,7 @@ Blockly.Blocks.procedures_callnoreturn = { * @this Blockly.Block */ getVarModels() { - return this.argumentVarModels_; + return this.argumentVarModels; }, /** * Add menu option to find the definition block for this call. @@ -304,7 +306,7 @@ Blockly.Blocks.procedures_callnoreturn = { } const name = this.getProcedureCall(); - const workspace = this.workspace; + const { workspace } = this; const option = { enabled: true }; option.text = translate('Highlight function definition'); @@ -318,15 +320,16 @@ Blockly.Blocks.procedures_callnoreturn = { options.push(option); }, - defType_: 'procedures_defnoreturn', + defType: 'procedures_defnoreturn', }; Blockly.JavaScript.procedures_callnoreturn = block => { + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE ); - const args = block.arguments_.map( + const args = block.arguments.map( (arg, i) => Blockly.JavaScript.valueToCode(block, `ARG${i}`, Blockly.JavaScript.ORDER_COMMA) || 'null' ); diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callreturn.js index 5178289d22..40ab5a0445 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callreturn.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_callreturn.js @@ -1,7 +1,7 @@ Blockly.Blocks.procedures_callreturn = { init() { - this.arguments_ = []; - this.previousDisabledState_ = false; + this.arguments = []; + this.previousDisabledState = false; this.jsonInit({ message0: '%1 %2', @@ -22,17 +22,17 @@ Blockly.Blocks.procedures_callreturn = { colourTertiary : Blockly.Colours.BinaryProcedures.colourTertiary, }); }, - onchange : Blockly.Blocks.procedures_callnoreturn.onchange, - getProcedureDefinition : Blockly.Blocks.procedures_callnoreturn.getProcedureDefinition, - getProcedureCall : Blockly.Blocks.procedures_callnoreturn.getProcedureCall, - renameProcedure : Blockly.Blocks.procedures_callnoreturn.renameProcedure, - setProcedureParameters_: Blockly.Blocks.procedures_callnoreturn.setProcedureParameters_, - updateShape_ : Blockly.Blocks.procedures_callnoreturn.updateShape_, - mutationToDom : Blockly.Blocks.procedures_callnoreturn.mutationToDom, - domToMutation : Blockly.Blocks.procedures_callnoreturn.domToMutation, - getVarModels : Blockly.Blocks.procedures_callnoreturn.getVarModels, - customContextMenu : Blockly.Blocks.procedures_callnoreturn.customContextMenu, - defType_ : 'procedures_defreturn', + onchange : Blockly.Blocks.procedures_callnoreturn.onchange, + getProcedureDefinition: Blockly.Blocks.procedures_callnoreturn.getProcedureDefinition, + getProcedureCall : Blockly.Blocks.procedures_callnoreturn.getProcedureCall, + renameProcedure : Blockly.Blocks.procedures_callnoreturn.renameProcedure, + setProcedureParameters: Blockly.Blocks.procedures_callnoreturn.setProcedureParameters, + updateShape : Blockly.Blocks.procedures_callnoreturn.updateShape, + mutationToDom : Blockly.Blocks.procedures_callnoreturn.mutationToDom, + domToMutation : Blockly.Blocks.procedures_callnoreturn.domToMutation, + getVarModels : Blockly.Blocks.procedures_callnoreturn.getVarModels, + customContextMenu : Blockly.Blocks.procedures_callnoreturn.customContextMenu, + defType : 'procedures_defreturn', }; Blockly.JavaScript.procedures_callreturn = Blockly.JavaScript.procedures_callnoreturn; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defnoreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defnoreturn.js index 6003706e3e..7598a0c9d7 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defnoreturn.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defnoreturn.js @@ -4,8 +4,8 @@ import { translate } from '../../../../../../../common/i18n'; Blockly.Blocks.procedures_defnoreturn = { init() { - this.arguments_ = []; - this.argumentVarModels_ = []; + this.arguments = []; + this.argumentVarModels = []; this.jsonInit({ message0: translate('function %1 %2'), @@ -31,11 +31,10 @@ Blockly.Blocks.procedures_defnoreturn = { nameField.setValidator(Blockly.Procedures.rename); // Render a ➕-icon for adding parameters - const fieldImage = new Blockly.FieldImage(plusIconLight, 24, 24, '+', () => this.onAddClick_()); + const fieldImage = new Blockly.FieldImage(plusIconLight, 24, 24, '+', () => this.onAddClick()); this.appendDummyInput('ADD_ICON').appendField(fieldImage); - this.setStatements_(true); - this.registerWorkspaceListener(); + this.setStatements(true); }, /** * Sets the block colour and updates this procedure's caller blocks @@ -64,7 +63,7 @@ Blockly.Blocks.procedures_defnoreturn = { * Prompt the user for parameter name * @this Blockly.Block */ - onAddClick_() { + onAddClick() { // Wrap in setTimeout so block doesn't stick to mouse (Blockly.Events.END_DRAG event isn't blocked). setTimeout(() => { const promptMessage = translate('Specify a parameter name:'); @@ -72,11 +71,11 @@ Blockly.Blocks.procedures_defnoreturn = { if (paramName) { const variable = Blockly.Variables.getOrCreateVariablePackage(this.workspace, null, paramName, ''); if (variable) { - this.arguments_.push(paramName); - this.argumentVarModels_.push(variable); + this.arguments.push(paramName); + this.argumentVarModels.push(variable); const paramField = this.getField('PARAMS'); - paramField.setText(`${translate('with: ')} ${this.arguments_.join(', ')}`); + paramField.setText(`${translate('with: ')} ${this.arguments.join(', ')}`); } } }); @@ -87,8 +86,8 @@ Blockly.Blocks.procedures_defnoreturn = { * @param {boolean} hasStatements True if a statement block is needed. * @this Blockly.Block */ - setStatements_(hasStatements) { - if (this.hasStatements_ === hasStatements) { + setStatements(hasStatements) { + if (this.hasStatements === hasStatements) { return; } @@ -101,18 +100,18 @@ Blockly.Blocks.procedures_defnoreturn = { this.removeInput('STACK', true); } - this.hasStatements_ = hasStatements; + this.hasStatements = hasStatements; }, /** * Update the display of parameters for this procedure definition block. * @private * @this Blockly.Block */ - updateParams_() { + updateParams() { let paramString = ''; - if (this.arguments_.length) { - paramString = `${translate('with:')} ${this.arguments_.join(', ')}`; + if (this.arguments.length) { + paramString = `${translate('with:')} ${this.arguments.join(', ')}`; } // The params field is deterministic based on the mutation, @@ -126,32 +125,32 @@ Blockly.Blocks.procedures_defnoreturn = { }, /** * Create XML to represent the argument inputs. - * @param {boolean=} opt_paramIds If true include the IDs of the parameter + * @param {boolean=} optParamIds If true include the IDs of the parameter * quarks. Used by Blockly.Procedures.mutateCallers for reconnection. * @return {!Element} XML storage element. * @this Blockly.Block */ - mutationToDom(opt_paramIds) { + mutationToDom(optParamIds) { const container = document.createElement('mutation'); - if (opt_paramIds) { + if (optParamIds) { container.setAttribute('name', this.getFieldValue('NAME')); } - this.argumentVarModels_.forEach((arg, i) => { + this.argumentVarModels.forEach((arg, i) => { const parameter = document.createElement('arg'); parameter.setAttribute('name', arg.name); parameter.setAttribute('varid', arg.getId()); - if (opt_paramIds && this.paramIds_) { - parameter.setAttribute('paramId', this.paramIds_[i]); + if (optParamIds && this.paramIds) { + parameter.setAttribute('paramId', this.paramIds[i]); } container.appendChild(parameter); }); // Save whether the statement input is visible. - if (!this.hasStatements_) { + if (!this.hasStatements) { container.setAttribute('statements', 'false'); } @@ -163,29 +162,30 @@ Blockly.Blocks.procedures_defnoreturn = { * @this Blockly.Block */ domToMutation(xmlElement) { - this.arguments_ = []; - this.argumentVarModels_ = []; + this.arguments = []; + this.argumentVarModels = []; - for (let i = 0, childNode; (childNode = xmlElement.childNodes[i]); i++) { + xmlElement.childNodes.forEach(childNode => { if (childNode.nodeName.toLowerCase() === 'arg') { const varName = childNode.getAttribute('name'); - this.arguments_.push(varName); + this.arguments.push(varName); const varId = childNode.getAttribute('varid') || childNode.getAttribute('varId'); const variable = Blockly.Variables.getOrCreateVariablePackage(this.workspace, varId, varName, ''); if (variable !== null) { - this.argumentVarModels_.push(variable); + this.argumentVarModels.push(variable); } else { + // eslint-disable-next-line no-console console.log(`Failed to create a variable with name ${varName}, ignoring.`); } } - } + }); - this.updateParams_(); + this.updateParams(); // Show or hide the statement input. - this.setStatements_(xmlElement.getAttribute('statements') !== 'false'); + this.setStatements(xmlElement.getAttribute('statements') !== 'false'); }, /** * Return the signature of this procedure definition. @@ -196,7 +196,7 @@ Blockly.Blocks.procedures_defnoreturn = { * @this Blockly.Block */ getProcedureDef() { - return [this.getFieldValue('NAME'), this.arguments_, false]; + return [this.getFieldValue('NAME'), this.arguments, false]; }, /** * Return all procedure callers related to this block. @@ -206,7 +206,7 @@ Blockly.Blocks.procedures_defnoreturn = { getProcedureCallers() { return this.workspace .getAllBlocks(false) - .filter(block => block.type === this.callType_ && block.data === this.id); + .filter(block => block.type === this.callType && block.data === this.id); }, /** * Return all variables referenced by this block. @@ -214,7 +214,7 @@ Blockly.Blocks.procedures_defnoreturn = { * @this Blockly.Block */ getVars() { - return this.arguments_; + return this.arguments; }, /** * Return all variables referenced by this block. @@ -222,7 +222,7 @@ Blockly.Blocks.procedures_defnoreturn = { * @this Blockly.Block */ getVarModels() { - return this.argumentVarModels_; + return this.argumentVarModels; }, /** * Add custom menu options to this block's context menu. @@ -234,62 +234,48 @@ Blockly.Blocks.procedures_defnoreturn = { return; } // Add option to create caller. - var option = { enabled: true }; - var name = this.getFieldValue('NAME'); + const option = { enabled: true }; + const name = this.getFieldValue('NAME'); option.text = Blockly.Msg.PROCEDURES_CREATE_DO.replace('%1', name); + const xmlMutation = document.createElement('mutation'); xmlMutation.setAttribute('name', name); - for (var i = 0; i < this.arguments_.length; i++) { + + this.arguments.forEach(argumentName => { const xmlArg = document.createElement('arg'); - xmlArg.setAttribute('name', this.arguments_[i]); + xmlArg.setAttribute('name', argumentName); xmlMutation.appendChild(xmlArg); - } - var xmlBlock = document.createElement('block'); - xmlBlock.setAttribute('type', this.callType_); + }); + + const xmlBlock = document.createElement('block'); + xmlBlock.setAttribute('type', this.callType); xmlBlock.appendChild(xmlMutation); option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); options.push(option); // Add options to create getters for each parameter. if (!this.isCollapsed()) { - for (var i = 0; i < this.argumentVarModels_.length; i++) { - var option = { enabled: true }; - const argVar = this.argumentVarModels_[i]; - var name = argVar.name; - option.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', name); - - const xmlField = Blockly.Variables.generateVariableFieldDom(argVar); - var xmlBlock = document.createElement('block'); - xmlBlock.setAttribute('type', 'variables_get'); - xmlBlock.appendChild(xmlField); - option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); - options.push(option); - } - } - }, - callType_: 'procedures_callnoreturn', - /** - * TODO: Registers an event to the workspace this procedure is on once. - * This listener will clean up variables that aren't used by any procedure - * and that aren't created by the user. This listener will persist through - * block deletion. - * @this Blockly.Block - */ - registerWorkspaceListener() { - if (!this.workspace || this.isInFlyout) { - return; - } + this.argumentVarModels.forEach(argumentVarModel => { + const getOption = { enabled: true }; + + getOption.text = Blockly.Msg.VARIABLES_SET_CREATE_GET.replace('%1', argumentVarModel.name); - const workspaceListener = event => {}; + const xmlField = Blockly.Variables.generateVariableFieldDom(argumentVarModel); + const xmlOptionBlock = document.createElement('block'); - const hasProcedureListener = this.workspace.listeners_.find(func => func.name === 'workspaceListener'); - if (!hasProcedureListener) { - this.workspace.addChangeListener(workspaceListener); + xmlOptionBlock.setAttribute('type', 'variables_get'); + xmlOptionBlock.appendChild(xmlField); + + getOption.callback = Blockly.ContextMenu.callbackFactory(this, xmlOptionBlock); + options.push(getOption); + }); } }, + callType: 'procedures_callnoreturn', }; Blockly.JavaScript.procedures_defnoreturn = block => { + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('NAME'), Blockly.Procedures.NAME_TYPE @@ -316,10 +302,11 @@ Blockly.JavaScript.procedures_defnoreturn = block => { returnValue = `${Blockly.JavaScript.INDENT}return ${returnValue};\n`; } - const args = block.arguments_.map(argumentName => - Blockly.JavaScript.variableDB_.getName(argumentName, Blockly.Variables.NAME_TYPE) + const args = block.arguments.map( + argumentName => Blockly.JavaScript.variableDB_.getName(argumentName, Blockly.Variables.NAME_TYPE) // eslint-disable-line no-underscore-dangle ); + // eslint-disable-next-line no-underscore-dangle const code = Blockly.JavaScript.scrub_( block, ` @@ -330,6 +317,7 @@ Blockly.JavaScript.procedures_defnoreturn = block => { ); // Add % so as not to collide with helper functions in definitions list. + // eslint-disable-next-line no-underscore-dangle Blockly.JavaScript.definitions_[`%${functionName}`] = code; return null; }; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defreturn.js index bd153d097f..45ec561e3b 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defreturn.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_defreturn.js @@ -3,8 +3,8 @@ import { plusIconDark } from '../../../images'; Blockly.Blocks.procedures_defreturn = { init() { - this.arguments_ = []; - this.argumentVarModels_ = []; + this.arguments = []; + this.argumentVarModels = []; this.jsonInit({ message0: translate('function %1 %2 %3'), @@ -42,19 +42,18 @@ Blockly.Blocks.procedures_defreturn = { nameField.setValidator(Blockly.Procedures.rename); // Render a ➕-icon for adding parameters - const fieldImage = new Blockly.FieldImage(plusIconDark, 24, 24, '+', () => this.onAddClick_()); + const fieldImage = new Blockly.FieldImage(plusIconDark, 24, 24, '+', () => this.onAddClick()); this.appendDummyInput('ADD_ICON').appendField(fieldImage); this.moveInputBefore('ADD_ICON', 'RETURN'); - this.setStatements_(true); - this.registerWorkspaceListener(); + this.setStatements(true); }, - onAddClick_ : Blockly.Blocks.procedures_defnoreturn.onAddClick_, - onchange : Blockly.Blocks.procedures_defnoreturn.onchange, - setStatements_: Blockly.Blocks.procedures_defnoreturn.setStatements_, - updateParams_ : Blockly.Blocks.procedures_defnoreturn.updateParams_, - mutationToDom : Blockly.Blocks.procedures_defnoreturn.mutationToDom, - domToMutation : Blockly.Blocks.procedures_defnoreturn.domToMutation, + onAddClick : Blockly.Blocks.procedures_defnoreturn.onAddClick, + onchange : Blockly.Blocks.procedures_defnoreturn.onchange, + setStatements: Blockly.Blocks.procedures_defnoreturn.setStatements, + updateParams : Blockly.Blocks.procedures_defnoreturn.updateParams, + mutationToDom: Blockly.Blocks.procedures_defnoreturn.mutationToDom, + domToMutation: Blockly.Blocks.procedures_defnoreturn.domToMutation, /** * Return the signature of this procedure definition. * @return {!Array} Tuple containing three elements: @@ -64,16 +63,16 @@ Blockly.Blocks.procedures_defreturn = { * @this Blockly.Block */ getProcedureDef() { - return [this.getFieldValue('NAME'), this.arguments_, true]; + return [this.getFieldValue('NAME'), this.arguments, true]; }, getProcedureCallers : Blockly.Blocks.procedures_defnoreturn.getProcedureCallers, getVars : Blockly.Blocks.procedures_defnoreturn.getVars, getVarModels : Blockly.Blocks.procedures_defnoreturn.getVarModels, renameVarById : Blockly.Blocks.procedures_defnoreturn.renameVarById, updateVarName : Blockly.Blocks.procedures_defnoreturn.updateVarName, - displayRenamedVar_ : Blockly.Blocks.procedures_defnoreturn.displayRenamedVar_, + displayRenamedVar : Blockly.Blocks.procedures_defnoreturn.displayRenamedVar, customContextMenu : Blockly.Blocks.procedures_defnoreturn.customContextMenu, - callType_ : 'procedures_callreturn', + callType : 'procedures_callreturn', registerWorkspaceListener: Blockly.Blocks.procedures_defnoreturn.registerWorkspaceListener, }; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_ifreturn.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_ifreturn.js index 80cfd1aa64..6008bb3c1b 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_ifreturn.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Functions/procedures_ifreturn.js @@ -7,7 +7,7 @@ import { setBlockTextColor } from '../../../../utils'; */ Blockly.Blocks.procedures_ifreturn = { init() { - this.hasReturnValue_ = true; + this.hasReturnValue = true; this.jsonInit({ message0: translate('if %1 return %2'), @@ -35,7 +35,7 @@ Blockly.Blocks.procedures_ifreturn = { */ mutationToDom() { const container = document.createElement('mutation'); - container.setAttribute('value', Number(this.hasReturnValue_)); + container.setAttribute('value', Number(this.hasReturnValue)); return container; }, /** @@ -45,9 +45,9 @@ Blockly.Blocks.procedures_ifreturn = { */ domToMutation(xmlElement) { const value = xmlElement.getAttribute('value'); - this.hasReturnValue_ = value === 1; + this.hasReturnValue = value === 1; - if (!this.hasReturnValue_) { + if (!this.hasReturnValue) { this.removeInput('VALUE'); this.appendDummyInput('VALUE').appendField(translate('return')); this.initSvg(); @@ -86,16 +86,16 @@ Blockly.Blocks.procedures_ifreturn = { }; // If needed, toggle whether this block has a return value. - if (block.type == 'procedures_defnoreturn' && this.hasReturnValue_) { + if (block.type === 'procedures_defnoreturn' && this.hasReturnValue) { this.removeInput('VALUE'); this.appendDummyInput('VALUE').appendField(translate('return')); rerender(); - this.hasReturnValue_ = false; - } else if (block.type == 'procedures_defreturn' && !this.hasReturnValue_) { + this.hasReturnValue = false; + } else if (block.type === 'procedures_defreturn' && !this.hasReturnValue) { this.removeInput('VALUE'); this.appendValueInput('VALUE').appendField(translate('return')); rerender(); - this.hasReturnValue_ = true; + this.hasReturnValue = true; } if (!this.isInFlyout) { @@ -117,7 +117,7 @@ Blockly.JavaScript.procedures_ifreturn = block => { const condition = Blockly.JavaScript.valueToCode(block, 'CONDITION', Blockly.JavaScript.ORDER_NONE) || 'false'; let branch; - if (block.hasReturnValue_) { + if (block.hasReturnValue) { const value = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_NONE) || 'null'; branch = `return ${value};\n`; } else { diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_create_with.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_create_with.js index 9e61a55c6f..d77a10252c 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_create_with.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_create_with.js @@ -64,6 +64,7 @@ Blockly.Blocks.lists_create_with = { Blockly.JavaScript.lists_create_with = block => { const variable = block.getFieldValue('VARIABLE'); + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName(variable, Blockly.Variables.NAME_TYPE); const elements = []; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getIndex.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getIndex.js index 0f2e1403e6..b0c4fca73f 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getIndex.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getIndex.js @@ -16,7 +16,7 @@ Blockly.Blocks.lists_getIndex = { ]; const modeMenu = new Blockly.FieldDropdown(this.MODE_OPTIONS, value => { const isStatement = value === 'REMOVE'; - this.updateStatement_(isStatement); + this.updateStatement(isStatement); }); this.appendValueInput('VALUE') @@ -24,6 +24,7 @@ Blockly.Blocks.lists_getIndex = { .appendField(translate('in list')); this.appendDummyInput().appendField(modeMenu, 'MODE'); this.appendDummyInput('AT'); + // eslint-disable-next-line no-underscore-dangle this.setColourFromRawValues_( Blockly.Colours.Binary.colour, Blockly.Colours.Binary.colourSecondary, @@ -31,7 +32,7 @@ Blockly.Blocks.lists_getIndex = { ); this.setOutput(true, null); - this.updateAt_(true); + this.updateAt(true); }, mutationToDom() { const container = document.createElement('mutation'); @@ -45,12 +46,12 @@ Blockly.Blocks.lists_getIndex = { }, domToMutation(xmlElement) { const isStatement = xmlElement.getAttribute('statement') === 'true'; - this.updateStatement_(isStatement); + this.updateStatement(isStatement); const isAt = xmlElement.getAttribute('at') !== 'false'; - this.updateAt_(isAt); + this.updateAt(isAt); }, - updateStatement_(newStatement) { + updateStatement(newStatement) { const oldStatement = !this.outputConnection; if (newStatement !== oldStatement) { @@ -64,7 +65,7 @@ Blockly.Blocks.lists_getIndex = { this.render(false); } }, - updateAt_(isAt) { + updateAt(isAt) { this.removeInput('AT', true); if (isAt) { @@ -76,7 +77,7 @@ Blockly.Blocks.lists_getIndex = { const menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, value => { const newAt = ['FROM_START', 'FROM_END'].includes(value); if (newAt !== isAt) { - this.updateAt_(newAt); + this.updateAt(newAt); this.setFieldValue(value, 'WHERE'); return null; } @@ -142,7 +143,9 @@ Blockly.JavaScript.lists_getIndex = block => { return `${list}.splice(${at}, 1);\n`; } } else if (where === 'RANDOM') { + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('listsGetRandomItem', [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(list, remove) { var x = Math.floor(Math.random() * list.length); if (remove) { diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getSublist.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getSublist.js index 198aff1c16..9b9396a5d3 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getSublist.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_getSublist.js @@ -17,6 +17,7 @@ Blockly.Blocks.lists_getSublist = { this.appendDummyInput('AT1'); this.appendDummyInput('AT2'); + // eslint-disable-next-line no-underscore-dangle this.setColourFromRawValues_( Blockly.Colours.Binary.colour, Blockly.Colours.Binary.colourSecondary, @@ -25,8 +26,8 @@ Blockly.Blocks.lists_getSublist = { this.setOutput(true, null); this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); - this.updateAt_(1, true); - this.updateAt_(2, true); + this.updateAt(1, true); + this.updateAt(2, true); }, mutationToDom() { const container = document.createElement('mutation'); @@ -41,10 +42,10 @@ Blockly.Blocks.lists_getSublist = { domToMutation(xmlElement) { const isAt1 = xmlElement.getAttribute('at1') === 'true'; const isAt2 = xmlElement.getAttribute('at2') === 'true'; - this.updateAt_(1, isAt1); - this.updateAt_(2, isAt2); + this.updateAt(1, isAt1); + this.updateAt(2, isAt2); }, - updateAt_(n, isAt) { + updateAt(n, isAt) { this.removeInput(`AT${n}`); if (isAt) { @@ -56,7 +57,7 @@ Blockly.Blocks.lists_getSublist = { const menu = new Blockly.FieldDropdown(this[`WHERE_OPTIONS_${n}`], value => { const newAt = ['FROM_START', 'FROM_END'].includes(value); if (newAt !== isAt) { - this.updateAt_(n, newAt); + this.updateAt(n, newAt); this.setFieldValue(value, `WHERE${n}`); return null; } @@ -99,17 +100,19 @@ Blockly.JavaScript.lists_getSublist = block => { code = `${list}.slice(${at1}, ${at2})`; } else { - const at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); - const at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); const wherePascalCase = { FROM_START: 'FromStart', FROM_END : 'FromEnd', }; const getIndex = (listName, where, at) => (where === 'FROM_END' ? `${listName}.length - 1 - ${at}` : at); + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_( `subsequence${wherePascalCase[where1]}${wherePascalCase[where2]}`, [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(sequence, at1, at2) { var start = ${getIndex('sequence', where1, 'at1')}; var end = ${getIndex('sequence', where2, 'at2')}; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_repeat.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_repeat.js index 2809e67b4f..fa1ac425ee 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_repeat.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_repeat.js @@ -29,11 +29,14 @@ Blockly.Blocks.lists_repeat = { }; Blockly.JavaScript.lists_repeat = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE ); + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('listsRepeat', [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(value, n) { var array = []; for (var i = 0; i < n; i++) { diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_setIndex.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_setIndex.js index d54e9d3fd6..74b0ac48e3 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_setIndex.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_setIndex.js @@ -18,6 +18,7 @@ Blockly.Blocks.lists_setIndex = { this.appendDummyInput('AT'); this.appendValueInput('TO').appendField(translate('as')); + // eslint-disable-next-line no-underscore-dangle this.setColourFromRawValues_( Blockly.Colours.Binary.colour, Blockly.Colours.Binary.colourSecondary, @@ -26,7 +27,7 @@ Blockly.Blocks.lists_setIndex = { this.setPreviousStatement(true, null); this.setNextStatement(true, null); - this.updateAt_(true); + this.updateAt(true); }, mutationToDom() { const container = document.createElement('mutation'); @@ -37,9 +38,9 @@ Blockly.Blocks.lists_setIndex = { }, domToMutation(xmlElement) { const isAt = xmlElement.getAttribute('at') !== 'false'; - this.updateAt_(isAt); + this.updateAt(isAt); }, - updateAt_(isAt) { + updateAt(isAt) { this.removeInput('AT', true); if (isAt) { @@ -51,7 +52,7 @@ Blockly.Blocks.lists_setIndex = { const menu = new Blockly.FieldDropdown(this.WHERE_OPTIONS, value => { const newAt = ['FROM_START', 'FROM_END'].includes(value); if (newAt !== isAt) { - this.updateAt_(newAt); + this.updateAt(newAt); this.setFieldValue(value, 'WHERE'); return null; } @@ -77,6 +78,7 @@ Blockly.JavaScript.lists_setIndex = block => { return ''; } + // eslint-disable-next-line no-underscore-dangle const listVar = Blockly.JavaScript.variableDB_.getDistinctName('tmpList', Blockly.Variables.NAME_TYPE); const code = `var ${listVar} = ${list};\n`; @@ -117,6 +119,7 @@ Blockly.JavaScript.lists_setIndex = block => { } else if (where === 'RANDOM') { code = cacheList(); + // eslint-disable-next-line no-underscore-dangle const xVar = Blockly.JavaScript.variableDB_.getDistinctName('tmpX', Blockly.Variables.NAME_TYPE); code += `var ${xVar} = Math.floor(Math.random() * ${list}.length);\n`; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_sort.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_sort.js index 064b25a20e..89592625b1 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_sort.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_sort.js @@ -37,7 +37,9 @@ Blockly.JavaScript.lists_sort = block => { const list = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '[]'; const direction = block.getFieldValue('DIRECTION') === 'ASCENDING' ? 1 : -1; const type = block.getFieldValue('TYPE'); + // eslint-disable-next-line no-underscore-dangle const getCompareFunctionName = Blockly.JavaScript.provideFunction_('listsGetSortCompare', [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(type, direction) { var compareFuncs = { "NUMERIC": function(a, b) { diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_split.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_split.js index 9a7bc25cdf..4d53ffb5c5 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_split.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_split.js @@ -4,7 +4,7 @@ Blockly.Blocks.lists_split = { init() { const dropdown = new Blockly.FieldDropdown( [[translate('make list from text'), 'SPLIT'], [translate('make text from list'), 'JOIN']], - newMode => this.updateType_(newMode) + newMode => this.updateType(newMode) ); this.appendValueInput('INPUT') @@ -18,6 +18,8 @@ Blockly.Blocks.lists_split = { this.setOutput(true, 'Array'); this.setOutputShape(Blockly.OUTPUT_SHAPE_ROUND); + + // eslint-disable-next-line no-underscore-dangle this.setColourFromRawValues_( Blockly.Colours.Binary.colour, Blockly.Colours.Binary.colourSecondary, @@ -30,9 +32,9 @@ Blockly.Blocks.lists_split = { return container; }, domToMutation(xmlElement) { - this.updateType_(xmlElement.getAttribute('mode')); + this.updateType(xmlElement.getAttribute('mode')); }, - updateType_(newMode) { + updateType(newMode) { const delimInput = this.getInput('DELIM'); const spaceField = this.getField('SPACE1'); diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_statement.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_statement.js index fe5c65479a..d4edcc033b 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/List/lists_statement.js @@ -10,8 +10,6 @@ Blockly.Blocks.lists_statement = { name: 'VALUE', }, ], - nextStatement : 'text_statement', - previousStatement: 'text_statement', colour : Blockly.Colours.BinaryLessGray.colour, colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_flow_statements.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_flow_statements.js index a2476298c1..588784deb8 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_flow_statements.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_flow_statements.js @@ -24,10 +24,6 @@ Blockly.Blocks.controls_flow_statements = { }; Blockly.JavaScript.controls_flow_statements = block => { - // Flow statements: continue, break. - if (block.getFieldValue('FLOW') === 'BREAK') { - return 'break;\n'; - } else if (block.getFieldValue('FLOW') === 'CONTINUE') { - return 'continue;\n'; - } + const keyword = block.getFieldValue('FLOW') === 'BREAK' ? 'break' : 'continue'; + return `${keyword};\n`; }; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js index c39163a0c5..525a6e89b0 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js @@ -43,6 +43,7 @@ Blockly.Blocks.controls_for = { }; Blockly.JavaScript.controls_for = block => { + // eslint-disable-next-line no-underscore-dangle const variable0 = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); const argument0 = Blockly.JavaScript.valueToCode(block, 'FROM', Blockly.JavaScript.ORDER_ASSIGNMENT) || '0'; const argument1 = Blockly.JavaScript.valueToCode(block, 'TO', Blockly.JavaScript.ORDER_ASSIGNMENT) || '0'; @@ -70,6 +71,7 @@ Blockly.JavaScript.controls_for = block => { // Cache non-trivial values to variables to prevent repeated look-ups. let startVar = argument0; if (!argument0.match(/^\w+$/) && !Blockly.isNumber(argument0)) { + // eslint-disable-next-line no-underscore-dangle startVar = Blockly.JavaScript.variableDB_.getDistinctName( `${variable0}_start`, Blockly.Variables.NAME_TYPE @@ -79,11 +81,13 @@ Blockly.JavaScript.controls_for = block => { let endVar = argument1; if (!argument1.match(/^\w+$/) && !Blockly.isNumber(argument1)) { + // eslint-disable-next-line no-underscore-dangle endVar = Blockly.JavaScript.variableDB_.getDistinctName(`${variable0}_end`, Blockly.Variables.NAME_TYPE); code += `var ${endVar} = ${argument1};\n`; } // Determine loop direction at start, in case one of the bounds changes during loop execution. + // eslint-disable-next-line no-underscore-dangle const incVar = Blockly.JavaScript.variableDB_.getDistinctName(`${variable0}_inc`, Blockly.Variables.NAME_TYPE); const incVal = Blockly.isNumber(increment) ? Math.abs(increment) : `Math.abs(${increment})`; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js index 8749124a7c..5232b2a255 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js @@ -33,6 +33,7 @@ Blockly.Blocks.controls_forEach = { }; Blockly.JavaScript.controls_forEach = block => { + // eslint-disable-next-line no-underscore-dangle const variable0 = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); const argument0 = Blockly.JavaScript.valueToCode(block, 'LIST', Blockly.JavaScript.ORDER_ASSIGNMENT) || '[]'; @@ -44,10 +45,12 @@ Blockly.JavaScript.controls_forEach = block => { // Cache non-trivial values to variables to prevent repeated look-ups. let listVar = argument0; if (!argument0.match(/^\w+$/)) { + // eslint-disable-next-line no-underscore-dangle listVar = Blockly.JavaScript.variableDB_.getDistinctName(`${variable0}_list`, Blockly.Variables.NAME_TYPE); code = `var ${listVar} = ${argument0};\n`; } + // eslint-disable-next-line no-underscore-dangle const indexVar = Blockly.JavaScript.variableDB_.getDistinctName(`${variable0}_list`, Blockly.Variables.NAME_TYPE); code += ` diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat_ext.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat_ext.js index 6aa1e640e3..0c8361233a 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat_ext.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_repeat_ext.js @@ -39,10 +39,12 @@ Blockly.JavaScript.controls_repeat_ext = block => { branch = Blockly.JavaScript.addLoopTrap(branch, block.id); let code = ''; + // eslint-disable-next-line no-underscore-dangle const loopVar = Blockly.JavaScript.variableDB_.getDistinctName('count', Blockly.Variables.NAME_TYPE); let endVar = repeats; if (!repeats.match(/^\w+$/) && !Blockly.isNumber(repeats)) { + // eslint-disable-next-line no-underscore-dangle endVar = Blockly.JavaScript.variableDB_.getDistinctName('repeat_end', Blockly.Variables.NAME_TYPE); code += `var ${endVar} = ${repeats}\n`; } diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_get.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_get.js index 0e1d1d95e6..7bdb3d4060 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_get.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_get.js @@ -23,6 +23,7 @@ Blockly.Blocks.variables_get = { }; Blockly.JavaScript.variables_get = block => { + // eslint-disable-next-line no-underscore-dangle const code = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); return [code, Blockly.JavaScript.ORDER_ATOMIC]; }; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_set.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_set.js index 244fc79f1b..a2b73ebcd5 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_set.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Variable/variables_set.js @@ -28,6 +28,7 @@ Blockly.Blocks.variables_set = { Blockly.JavaScript.variables_set = block => { const argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE', Blockly.JavaScript.ORDER_ASSIGNMENT) || '0'; + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); const code = `${varName} = ${argument0};\n`; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/check_result.js b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/check_result.js index 1bfdf33868..d67f8480f9 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/check_result.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/check_result.js @@ -1,4 +1,3 @@ -import { insideAfterPurchase } from '../../../../relationChecker'; import config from '../../../../../../common/const'; import { translate } from '../../../../../../../common/utils/tools'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/read_details.js b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/read_details.js index 1d8b7f5568..49a4bc23bf 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/read_details.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/After Purchase/read_details.js @@ -1,4 +1,3 @@ -import { insideAfterPurchase } from '../../../../relationChecker'; import config from '../../../../../../common/const'; import { translate } from '../../../../../../../common/utils/tools'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/ask_price.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/ask_price.js index 9f21b60ae8..69f19d9e6a 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/ask_price.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/ask_price.js @@ -1,4 +1,3 @@ -import { insideBeforePurchase } from '../../../../relationChecker'; import { getPurchaseChoices } from '../../../shared'; import { translate } from '../../../../../../../common/i18n'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/purchase.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/purchase.js index 5af531d642..ea8e258708 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/purchase.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Before Purchase/purchase.js @@ -1,4 +1,3 @@ -import { insideBeforePurchase } from '../../../../relationChecker'; import { getPurchaseChoices, updatePurchaseChoices } from '../../../shared'; import { translate } from '../../../../../../../common/i18n'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/check_sell.js b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/check_sell.js index c6deda731c..4277287757 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/check_sell.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/check_sell.js @@ -1,4 +1,3 @@ -import { insideDuringPurchase } from '../../../../relationChecker'; import { translate } from '../../../../../../../common/i18n'; Blockly.Blocks.check_sell = { diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_at_market.js b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_at_market.js index 5f180c56a3..60fde66ab2 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_at_market.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/During Purchase/sell_at_market.js @@ -1,4 +1,3 @@ -import { insideDuringPurchase } from '../../../../relationChecker'; import { translate } from '../../../../../../../common/utils/tools'; Blockly.Blocks.sell_at_market = { diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bb_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bb_statement.js index 70368cf5bc..0083801a83 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bb_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bb_statement.js @@ -75,6 +75,7 @@ Blockly.Blocks.bb_statement = { }; Blockly.JavaScript.bb_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bba_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bba_statement.js index f8d7b37b96..0c7c37b765 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bba_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/bba_statement.js @@ -41,6 +41,7 @@ Blockly.Blocks.bba_statement = { }; Blockly.JavaScript.bba_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/ema_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/ema_statement.js index c17ca64305..e479cc7709 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/ema_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/ema_statement.js @@ -35,6 +35,7 @@ Blockly.Blocks.ema_statement = { }; Blockly.JavaScript.ema_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/emaa_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/emaa_statement.js index e9eed783ad..95f2d2951a 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/emaa_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/emaa_statement.js @@ -35,6 +35,7 @@ Blockly.Blocks.emaa_statement = { }; Blockly.JavaScript.emaa_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/macda_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/macda_statement.js index d99386f4e7..ffc023a91e 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/macda_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/macda_statement.js @@ -41,6 +41,7 @@ Blockly.Blocks.macda_statement = { }; Blockly.JavaScript.macda_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsi_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsi_statement.js index e32de5493a..62bc2e0ac9 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsi_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsi_statement.js @@ -35,6 +35,7 @@ Blockly.Blocks.rsi_statement = { }; Blockly.JavaScript.rsi_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsia_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsia_statement.js index 5c72a1d11c..7d0e49ee5e 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsia_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/rsia_statement.js @@ -35,6 +35,7 @@ Blockly.Blocks.rsia_statement = { }; Blockly.JavaScript.rsia_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/sma_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/sma_statement.js index f561be6495..9971112601 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/sma_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/sma_statement.js @@ -35,6 +35,7 @@ Blockly.Blocks.sma_statement = { }; Blockly.JavaScript.sma_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/smaa_statement.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/smaa_statement.js index ce593225e4..ceb69f187f 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/smaa_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Indicators/smaa_statement.js @@ -34,6 +34,7 @@ Blockly.Blocks.smaa_statement = { }; Blockly.JavaScript.smaa_statement = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./loader.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./loader.js index 4d07e477fb..51590fdf58 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./loader.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Tools/Misc./loader.js @@ -62,6 +62,7 @@ Blockly.Blocks.loader = { Blockly.JavaScript.loader = block => { if (block.loadedVariables.length) { + // eslint-disable-next-line no-underscore-dangle return `var ${block.loadedVariables.map(v => Blockly.JavaScript.variableDB_.safeName_(v)).toString()}`; } return ''; diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition.js index bdd64d1312..b583dd0bde 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition.js @@ -1,12 +1,11 @@ -import { translate } from '../../../../../../../common/i18n'; -import { setBlockTextColor, hideEventsFromBlockly } from '../../../../utils'; import { defineContract } from '../../../images'; +import { setBlockTextColor } from '../../../../utils'; import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/i18n'; Blockly.Blocks.trade_definition = { init() { this.jsonInit({ - type : 'trade_definition', message0: translate('%1 (1) Define your trade contract %2'), message1: '%1', message2: translate('Run Once at Start: %1'), @@ -101,7 +100,9 @@ Blockly.Blocks.trade_definition = { } }); - const paramsToReadd = this.requiredParamBlocks.filter(blockName => blocksInStatement.findIndex(block => block.type === blockName) === -1); + const paramsToReadd = this.requiredParamBlocks.filter( + blockName => blocksInStatement.findIndex(block => block.type === blockName) === -1 + ); paramsToReadd.forEach(blockName => { Blockly.Events.disable(); diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_contracttype.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_contracttype.js index 212bd14284..a04cd3cead 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_contracttype.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_contracttype.js @@ -5,7 +5,6 @@ import config from '../../../../../../common/const'; Blockly.Blocks.trade_definition_contracttype = { init() { this.jsonInit({ - type : 'trade_definition', message0: 'Contract Type: %1', args0 : [ { @@ -14,8 +13,6 @@ Blockly.Blocks.trade_definition_contracttype = { options: [['', '']], }, ], - previousStatement: 'trade_definition', - nextStatement : 'trade_definition', colour : Blockly.Colours.BinaryLessPurple.colour, colourSecondary : Blockly.Colours.Binary.colourSecondary, colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_market.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_market.js index 800f01fb7d..5231aa533a 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_market.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_market.js @@ -3,7 +3,6 @@ import { fieldGeneratorMapping } from '../../../shared'; Blockly.Blocks.trade_definition_market = { init() { this.jsonInit({ - type : 'trade_definition', message0: 'Market: %1 Submarket: %2 Symbol: %3', args0 : [ { @@ -22,8 +21,6 @@ Blockly.Blocks.trade_definition_market = { options: [['', '']], }, ], - previousStatement: 'trade_definition', - nextStatement : 'trade_definition', colour : Blockly.Colours.BinaryLessPurple.colour, colourSecondary : Blockly.Colours.Binary.colourSecondary, colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartbuysell.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartbuysell.js index 730bb82c8e..0b094269ce 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartbuysell.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartbuysell.js @@ -1,7 +1,6 @@ Blockly.Blocks.trade_definition_restartbuysell = { init() { this.jsonInit({ - type : 'trade_definition', message0: 'Restart buy/sell on error (disable for better performance): %1', args0 : [ { @@ -10,8 +9,6 @@ Blockly.Blocks.trade_definition_restartbuysell = { check: 'Boolean', }, ], - previousStatement: 'trade_definition', - nextStatement : 'trade_definition', colour : Blockly.Colours.BinaryLessPurple.colour, colourSecondary : Blockly.Colours.Binary.colourSecondary, colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartonerror.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartonerror.js index df4afdb394..1ad1235863 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartonerror.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_restartonerror.js @@ -1,7 +1,6 @@ Blockly.Blocks.trade_definition_restartonerror = { init() { this.jsonInit({ - type : 'trade_definition', message0: 'Restart last trade on error (bot ignores the unsuccessful trade): %1', args0 : [ { @@ -10,8 +9,6 @@ Blockly.Blocks.trade_definition_restartonerror = { check: 'Boolean', }, ], - previousStatement: 'trade_definition', - nextStatement : 'trade_definition', colour : Blockly.Colours.BinaryLessPurple.colour, colourSecondary : Blockly.Colours.Binary.colourSecondary, colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradeoptions.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradeoptions.js index a2de4e6df2..845886787d 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradeoptions.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradeoptions.js @@ -1,12 +1,10 @@ -import { translate } from '../../../../../../../common/i18n'; import { getBarriersForContracts, pollForContracts, getDurationsForContracts } from '../../../shared'; -import { findTopParentBlock, hideEventsFromBlockly } from '../../../../utils'; import config from '../../../../../../common/const'; +import { translate } from '../../../../../../../common/i18n'; Blockly.Blocks.trade_definition_tradeoptions = { init() { this.jsonInit({ - type : 'trade_definition', message0: translate('Duration: %1 %2 Stake: %3 %4'), args0 : [ { @@ -90,26 +88,28 @@ Blockly.Blocks.trade_definition_tradeoptions = { } }, createPredictionInput(predictionRange) { - hideEventsFromBlockly(() => { - if (this.getInput('PREDICTION')) { - return; - } + Blockly.Events.disable(); - this.appendDummyInput('PREDICTION_LABEL').appendField(translate('Prediction:')); + if (this.getInput('PREDICTION')) { + return; + } - const predictionInput = this.appendValueInput('PREDICTION'); + this.appendDummyInput('PREDICTION_LABEL').appendField(translate('Prediction:')); - // We can't determine which contract a user buys, so sometimes the prediction range - // returned may not be valid, start at minimum index 1 (if possible) to bypass that - const index = Math.min(1, predictionRange.length - 1); - const shadowBlock = this.workspace.newBlock('math_number'); + const predictionInput = this.appendValueInput('PREDICTION'); - shadowBlock.setShadow(true); - shadowBlock.setFieldValue(predictionRange[index], 'NUM'); - shadowBlock.outputConnection.connect(predictionInput.connection); - shadowBlock.initSvg(); - shadowBlock.render(true); - }); + // We can't determine which contract a user buys, so sometimes the prediction range + // returned may not be valid, start at minimum index 1 (if possible) to bypass that + const index = Math.min(1, predictionRange.length - 1); + const shadowBlock = this.workspace.newBlock('math_number'); + + shadowBlock.setShadow(true); + shadowBlock.setFieldValue(predictionRange[index], 'NUM'); + shadowBlock.outputConnection.connect(predictionInput.connection); + shadowBlock.initSvg(); + shadowBlock.render(true); + + Blockly.Events.enable(); }, createBarrierInput(barriers, startIndex = 0) { const inputNames = ['BARRIER', 'SECONDBARRIER']; @@ -213,6 +213,7 @@ Blockly.Blocks.trade_definition_tradeoptions = { } else if (hasFirstBarrier) { this.createBarrierInput({ values: [1] }); } else if (hasPrediction) { + this.createPredictionInput([1]); } }, // Export mutations to XML diff --git a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradetype.js b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradetype.js index f42f217acf..cffad53cd4 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradetype.js +++ b/src/botPage/view/blockly/blocks/Scratch/Binary/Trade Definition/trade_definition_tradetype.js @@ -11,7 +11,6 @@ import { translate } from '../../../../../../../common/utils/tools'; Blockly.Blocks.trade_definition_tradetype = { init() { this.jsonInit({ - type : 'trade_definition', message0: translate('Trade Category: %1 Trade Type: %2'), args0 : [ { @@ -25,8 +24,6 @@ Blockly.Blocks.trade_definition_tradetype = { options: [['', '']], }, ], - previousStatement: 'trade_definition', - nextStatement : 'trade_definition', colour : Blockly.Colours.BinaryLessPurple.colour, colourSecondary : Blockly.Colours.Binary.colourSecondary, colourTertiary : Blockly.Colours.BinaryLessPurple.colourTertiary, diff --git a/src/botPage/view/blockly/blocks/Scratch/Logic/controls_if.js b/src/botPage/view/blockly/blocks/Scratch/Logic/controls_if.js index a60f3725b8..2fe3f7fce9 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Logic/controls_if.js +++ b/src/botPage/view/blockly/blocks/Scratch/Logic/controls_if.js @@ -3,8 +3,8 @@ import { translate } from '../../../../../../common/i18n'; Blockly.Blocks.controls_if = { init() { - this.elseIfCount_ = 0; - this.elseCount_ = 0; + this.elseIfCount = 0; + this.elseCount = 0; this.jsonInit({ message0: translate('if %1 then'), @@ -30,7 +30,7 @@ Blockly.Blocks.controls_if = { nextStatement : null, }); - const addInputIcon = this.getAddInputIcon_(); + const addInputIcon = this.getAddInputIcon(); this.appendDummyInput('MUTATOR').appendField(addInputIcon); }, /** @@ -39,17 +39,17 @@ Blockly.Blocks.controls_if = { * @this Blockly.Block */ mutationToDom() { - if (!this.elseIfCount_ && !this.elseCount_) { + if (!this.elseIfCount && !this.elseCount) { return null; } const container = document.createElement('mutation'); - if (this.elseIfCount_) { - container.setAttribute('elseif', this.elseIfCount_); + if (this.elseIfCount) { + container.setAttribute('elseif', this.elseIfCount); } - if (this.elseCount_) { + if (this.elseCount) { container.setAttribute('else', 1); } @@ -61,12 +61,12 @@ Blockly.Blocks.controls_if = { * @this Blockly.Block */ domToMutation(xmlElement) { - this.elseIfCount_ = parseInt(xmlElement.getAttribute('elseif'), 10) || 0; - this.elseCount_ = parseInt(xmlElement.getAttribute('else'), 10) || 0; + this.elseIfCount = parseInt(xmlElement.getAttribute('elseif')) || 0; + this.elseCount = parseInt(xmlElement.getAttribute('else')) || 0; - this.updateShape_(); + this.updateShape(); }, - updateShape_() { + updateShape() { // Delete everything. if (this.getInput('ELSE')) { this.removeInput('ELSE'); @@ -85,51 +85,51 @@ Blockly.Blocks.controls_if = { } // Rebuild block - for (let i = 1; i <= this.elseIfCount_; i++) { - this.appendDummyInput(`IF_LABEL${i}`).appendField(translate('else if')); - this.appendValueInput(`IF${i}`).setCheck('Boolean'); - this.appendDummyInput(`THEN_LABEL${i}`).appendField(translate('then')); - this.appendDummyInput(`DELETE_ICON${i}`).appendField(this.getRemoveInputIcon_(i, false)); - this.appendStatementInput(`DO${i}`); + for (let j = 1; j <= this.elseIfCount; j++) { + this.appendDummyInput(`IF_LABEL${j}`).appendField(translate('else if')); + this.appendValueInput(`IF${j}`).setCheck('Boolean'); + this.appendDummyInput(`THEN_LABEL${j}`).appendField(translate('then')); + this.appendDummyInput(`DELETE_ICON${j}`).appendField(this.getRemoveInputIcon(j, false)); + this.appendStatementInput(`DO${j}`); } - if (this.elseCount_) { + if (this.elseCount) { this.appendDummyInput('ELSE_LABEL').appendField(translate('else')); - this.appendDummyInput('DELETE_ELSE').appendField(this.getRemoveInputIcon_(this.elseIfCount_ + 1, true)); + this.appendDummyInput('DELETE_ELSE').appendField(this.getRemoveInputIcon(this.elseIfCount + 1, true)); this.appendStatementInput('ELSE'); } - this.appendDummyInput('MUTATOR').appendField(this.getAddInputIcon_()); + this.appendDummyInput('MUTATOR').appendField(this.getAddInputIcon()); this.initSvg(); this.render(); }, - getAddInputIcon_() { + getAddInputIcon() { const onAddClick = () => { if (!this.workspace || this.isInFlyout) { return; } - const newInputNum = this.elseIfCount_ + 1; + const newInputNum = this.elseIfCount + 1; - if (this.elseCount_ === 0) { + if (this.elseCount === 0) { // No `elseif`, just add an `else`-statement this.appendDummyInput('ELSE_LABEL').appendField(translate('else')); - this.appendDummyInput('DELETE_ELSE').appendField(this.getRemoveInputIcon_(newInputNum, true)); + this.appendDummyInput('DELETE_ELSE').appendField(this.getRemoveInputIcon(newInputNum, true)); this.appendStatementInput('ELSE'); - this.elseCount_++; + this.elseCount++; } else { // We've already got `elseif` + `else`, keep adding more `elseif`'s this.appendDummyInput(`IF_LABEL${newInputNum}`).appendField(translate('else if')); this.appendValueInput(`IF${newInputNum}`).setCheck('Boolean'); this.appendDummyInput(`THEN_LABEL${newInputNum}`).appendField(translate('then')); this.appendDummyInput(`DELETE_ICON${newInputNum}`).appendField( - this.getRemoveInputIcon_(newInputNum, false) + this.getRemoveInputIcon(newInputNum, false) ); this.appendStatementInput(`DO${newInputNum}`); - this.elseIfCount_++; + this.elseIfCount++; } // We already have an else, this input needs to be moved to the bottom where it belongs. @@ -149,7 +149,7 @@ Blockly.Blocks.controls_if = { const fieldImage = new Blockly.FieldImage(plusIconDark, 24, 24, '+', onAddClick); return fieldImage; }, - getRemoveInputIcon_(index, isElseStack) { + getRemoveInputIcon(index, isElseStack) { const onRemoveClick = () => { if (!this.workspace || this.isInFlyout) { return; @@ -159,7 +159,7 @@ Blockly.Blocks.controls_if = { this.removeInput('ELSE_LABEL'); this.removeInput('DELETE_ELSE'); this.removeInput('ELSE'); - this.elseCount_ = 0; + this.elseCount = 0; } else { // Determine which label it is, has to be done inside this function. const inputNames = ['IF_LABEL', 'IF', 'THEN_LABEL', 'DELETE_ICON', 'DO']; @@ -180,12 +180,13 @@ Blockly.Blocks.controls_if = { // Re-attach click handler with correct index. if (inputName === 'DELETE_ICON') { - largerInput.fieldRow.forEach((field, fieldIndex) => { + for (let k = 0; k < largerInput.fieldRow.length; k++) { + const field = largerInput.fieldRow[k]; field.dispose(); - largerInput.fieldRow.splice(fieldIndex, 1); - }); + largerInput.fieldRow.splice(k, 1); + } - largerInput.appendField(this.getRemoveInputIcon_(newIndex, false)); + largerInput.appendField(this.getRemoveInputIcon(newIndex, false)); } i++; @@ -195,7 +196,7 @@ Blockly.Blocks.controls_if = { } }); - this.elseIfCount_--; + this.elseIfCount--; } }; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_change.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_change.js index f5a3dfa2ea..7f30b16205 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Math/math_change.js +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_change.js @@ -27,6 +27,7 @@ Blockly.Blocks.math_change = { Blockly.JavaScript.math_change = block => { const variable = block.getFieldValue('VAR'); + // eslint-disable-next-line no-underscore-dangle const argument0 = Blockly.JavaScript.variableDB_.getName(variable, Blockly.Variables.NAME_TYPE); const argument1 = Blockly.JavaScript.valueToCode(block, 'DELTA', Blockly.JavaScript.ORDER_ADDITION) || '0'; diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_number_property.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_number_property.js index b8ad110055..0b12a2f34e 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Math/math_number_property.js +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_number_property.js @@ -69,7 +69,9 @@ Blockly.JavaScript.math_number_property = block => { let code; if (property === 'PRIME') { + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('mathIsPrime', [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}_(n) { // https://en.wikipedia.org/wiki/Primality_test#Naive_methods if (n == 2 || n == 3) { diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_on_list.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_on_list.js index 10de9f13ac..587882a6d5 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Math/math_on_list.js +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_on_list.js @@ -33,6 +33,7 @@ Blockly.Blocks.math_on_list = { }, }; +/* eslint-disable no-underscore-dangle */ Blockly.JavaScript.math_on_list = block => { const operation = block.getFieldValue('OPERATION'); diff --git a/src/botPage/view/blockly/blocks/Scratch/Math/math_random_int.js b/src/botPage/view/blockly/blocks/Scratch/Math/math_random_int.js index e8f9fa98e2..720268a542 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Math/math_random_int.js +++ b/src/botPage/view/blockly/blocks/Scratch/Math/math_random_int.js @@ -29,7 +29,9 @@ Blockly.JavaScript.math_random_int = block => { const argument0 = Blockly.JavaScript.valueToCode(block, 'FROM', Blockly.JavaScript.ORDER_COMMA) || '0'; const argument1 = Blockly.JavaScript.valueToCode(block, 'TO', Blockly.JavaScript.ORDER_COMMA) || '0'; + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('mathRandomInt', [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(a, b) { if (a > b) { // Swap a and b to ensure a is smaller. diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text.js b/src/botPage/view/blockly/blocks/Scratch/Text/text.js index 3425cb0638..90cce4b4e9 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Text/text.js +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text.js @@ -18,6 +18,7 @@ Blockly.Blocks.text = { }; Blockly.JavaScript.text = block => { + // eslint-disable-next-line no-underscore-dangle const code = Blockly.JavaScript.quote_(block.getFieldValue('TEXT')); return [code, Blockly.JavaScript.ORDER_ATOMIC]; }; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_append.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_append.js index 9c26bcf0da..8d26147836 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Text/text_append.js +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_append.js @@ -24,29 +24,19 @@ Blockly.Blocks.text_append = { }, }; -/** - * Enclose the provided value in 'String(...)' function. - * Leave string literals alone. - * @param {string} value Code evaluating to a value. - * @return {string} Code evaluating to a string. - * @private - */ -Blockly.JavaScript.text.forceString_ = function(value) { - if (Blockly.JavaScript.text.forceString_.strRegExp.test(value)) { - return value; - } - return `String(${value})`; -}; - -/** - * Regular expression to detect a single-quoted string literal. - */ -Blockly.JavaScript.text.forceString_.strRegExp = /^\s*'([^']|\\')*'\s*$/; - Blockly.JavaScript.text_append = block => { + const forceString = value => { + const strRegExp = /^\s*'([^']|\\')*'\s*$/; + if (strRegExp.test(value)) { + return value; + } + return `String(${value})`; + }; + + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName(block.getFieldValue('VAR'), Blockly.Variables.NAME_TYPE); const value = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_NONE) || '\'\''; - const code = `${varName} += ${Blockly.JavaScript.text.forceString_(value)};\n`; + const code = `${varName} += ${forceString(value)};\n`; return code; }; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_changeCase.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_changeCase.js index c7efaf3687..eafac9b855 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Text/text_changeCase.js +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_changeCase.js @@ -43,7 +43,9 @@ Blockly.JavaScript.text_changeCase = block => { if (operator) { code = `${text}${operator}`; } else { + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('textToTitleCase', [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(str) { return str.toLowerCase().split(' ').map(function(word) { return word.replace(word[0], word[0].toUpperCase()); diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_charAt.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_charAt.js index 1d9464692f..803fd281f2 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Text/text_charAt.js +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_charAt.js @@ -31,32 +31,32 @@ Blockly.Blocks.text_charAt = { const dropdown = this.getField('WHERE'); dropdown.setValidator(value => { const newAt = ['FROM_START', 'FROM_END'].includes(value); - if (newAt !== this.isAt_) { - this.updateAt_(newAt); + if (newAt !== this.isAt) { + this.updateAt(newAt); this.setFieldValue(value, 'WHERE'); return null; } return undefined; }); - this.updateAt_(true); + this.updateAt(true); }, mutationToDom() { const container = document.createElement('mutation'); - container.setAttribute('at', !!this.isAt_); + container.setAttribute('at', !!this.isAt); return container; }, domToMutation(xmlElement) { - const isAt = xmlElement.getAttribute('at') != 'false'; - this.updateAt_(isAt); + const isAt = xmlElement.getAttribute('at') !== 'false'; + this.updateAt(isAt); }, - updateAt_(isAt) { + updateAt(isAt) { this.removeInput('AT', true); if (isAt) { this.appendValueInput('AT').setCheck('Number'); } - this.isAt_ = isAt; + this.isAt = isAt; this.initSvg(); this.render(false); }, @@ -81,7 +81,9 @@ Blockly.JavaScript.text_charAt = block => { } else if (where === 'LAST') { code = `${text}.slice(-1)`; } else if (where === 'RANDOM') { + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('textRandomLetter', [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(text) { var x = Math.floor(Math.random() * text.length); return text[x]; diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_getSubstring.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_getSubstring.js index dcd887f303..20bcaf0d12 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Text/text_getSubstring.js +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_getSubstring.js @@ -46,8 +46,8 @@ Blockly.Blocks.text_getSubstring = { colourTertiary : Blockly.Colours.Binary.colourTertiary, }); - this.updateAt_(1, true); - this.updateAt_(2, true); + this.updateAt(1, true); + this.updateAt(2, true); }, mutationToDom() { const container = document.createElement('mutation'); @@ -63,10 +63,10 @@ Blockly.Blocks.text_getSubstring = { const isAt1 = xmlElement.getAttribute('at1') === 'true'; const isAt2 = xmlElement.getAttribute('at2') === 'true'; - this.updateAt_(1, isAt1); - this.updateAt_(2, isAt2); + this.updateAt(1, isAt1); + this.updateAt(2, isAt2); }, - updateAt_(n, isAt) { + updateAt(n, isAt) { this.removeInput(`AT${n}`, true); if (isAt) { this.appendValueInput(`AT${n}`).setCheck('Number'); @@ -77,7 +77,7 @@ Blockly.Blocks.text_getSubstring = { const menu = new Blockly.FieldDropdown(this[`WHERE_OPTIONS_${n}`], value => { const newAt = ['FROM_START', 'FROM_END'].includes(value); if (newAt !== isAt) { - this.updateAt_(n, newAt); + this.updateAt(n, newAt); this.setFieldValue(value, `WHERE${n}`); return null; } @@ -127,22 +127,24 @@ Blockly.JavaScript.text_getSubstring = block => { at2 = `${text}.length`; } } else { - const at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); - const at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); - const getIndex_ = Blockly.JavaScript.text.getIndex_; + at1 = Blockly.JavaScript.getAdjusted(block, 'AT1'); + at2 = Blockly.JavaScript.getAdjusted(block, 'AT2'); + const { getIndex_ } = Blockly.JavaScript.text; const wherePascalCase = { FIRST : 'First', LAST : 'Last', FROM_START: 'FromStart', FROM_END : 'FromEnd', }; + // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_( `subsequence${wherePascalCase[where1]}${wherePascalCase[where2]}`, [ + // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}( sequence - ${where1 == 'FROM_END' || where1 == 'FROM_START' ? ', at1' : ''} - ${where2 == 'FROM_END' || where2 == 'FROM_START' ? ', at2' : ''} + ${where1 === 'FROM_END' || where1 === 'FROM_START' ? ', at1' : ''} + ${where2 === 'FROM_END' || where2 === 'FROM_START' ? ', at2' : ''} ) { var start = ${getIndex_('sequence', where1, 'at1')}; var end = ${getIndex_('sequence', where2, 'at2')}; @@ -153,8 +155,8 @@ Blockly.JavaScript.text_getSubstring = block => { code = `${functionName}( ${text} - ${where1 == 'FROM_END' || where1 == 'FROM_START' ? `, ${at1}` : ''} - ${where2 == 'FROM_END' || where2 == 'FROM_START' ? `, ${at2}` : ''} + ${where1 === 'FROM_END' || where1 === 'FROM_START' ? `, ${at1}` : ''} + ${where2 === 'FROM_END' || where2 === 'FROM_START' ? `, ${at2}` : ''} )`; } diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_join.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_join.js index 8946882672..f56dc2b50f 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Text/text_join.js +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_join.js @@ -91,6 +91,7 @@ Blockly.Blocks.text_join = { }; Blockly.JavaScript.text_join = block => { + // eslint-disable-next-line no-underscore-dangle const varName = Blockly.JavaScript.variableDB_.getName( block.getFieldValue('VARIABLE'), Blockly.Variables.NAME_TYPE diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_prompt_ext.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_prompt_ext.js index 4ca40068ea..93424e30ce 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Text/text_prompt_ext.js +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_prompt_ext.js @@ -44,6 +44,7 @@ Blockly.JavaScript.text_prompt_ext = block => { if (block.getField('TEXT')) { // Internal message + // eslint-disable-next-line no-underscore-dangle msg = Blockly.JavaScript.quote_(block.getFieldValue('TEXT')); } else { // External message diff --git a/src/botPage/view/blockly/blocks/Scratch/Text/text_statement.js b/src/botPage/view/blockly/blocks/Scratch/Text/text_statement.js index f25c47d225..d106490bda 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Text/text_statement.js +++ b/src/botPage/view/blockly/blocks/Scratch/Text/text_statement.js @@ -1,4 +1,4 @@ -import { minusIconDark, lockIconDark } from '../../images'; +import { minusIconDark } from '../../images'; Blockly.Blocks.text_statement = { init() { @@ -10,8 +10,6 @@ Blockly.Blocks.text_statement = { name: 'TEXT', }, ], - nextStatement : 'text_statement', - previousStatement: 'text_statement', colour : Blockly.Colours.BinaryLessGray.colour, colourSecondary : Blockly.Colours.BinaryLessGray.colourSecondary, colourTertiary : Blockly.Colours.BinaryLessGray.colourTertiary, diff --git a/src/botPage/view/blockly/blocks/before_purchase/purchase.js b/src/botPage/view/blockly/blocks/before_purchase/purchase.js index 7fb72bf38b..167845a135 100644 --- a/src/botPage/view/blockly/blocks/before_purchase/purchase.js +++ b/src/botPage/view/blockly/blocks/before_purchase/purchase.js @@ -14,7 +14,7 @@ Blockly.Blocks.purchase = { this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); }, onchange: function onchange(ev) { - // insideBeforePurchase(this, ev, 'Purchase'); + insideBeforePurchase(this, ev, 'Purchase'); }, }; Blockly.JavaScript.purchase = block => { diff --git a/src/botPage/view/blockly/blocks/index.js b/src/botPage/view/blockly/blocks/index.js index 1dc06f52e9..246822b022 100644 --- a/src/botPage/view/blockly/blocks/index.js +++ b/src/botPage/view/blockly/blocks/index.js @@ -1,10 +1,4 @@ -// import './tools'; -// import './ticks'; -// import './before_purchase'; -// import './during_purchase'; -// import './after_purchase'; -// import './indicators'; - +/* eslint-disable */ import './Scratch/Logic'; import './Scratch/Math'; import './Scratch/Text'; @@ -24,3 +18,4 @@ import './Scratch/Binary/Trade Definition'; import './Scratch/Binary/Before Purchase'; import './Scratch/Binary/During Purchase'; import './Scratch/Binary/After Purchase'; +/* eslint-enable */ From 21b12672e994f093ebc0c39c53a0ff3b227231dd Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 11:54:13 +0800 Subject: [PATCH 15/74] Blockly/Scratch overrides --- src/botPage/view/blockly/overrides/block.js | 2 + .../view/blockly/overrides/block_svg.js | 7 +- .../view/blockly/overrides/contextmenu.js | 3 +- .../view/blockly/overrides/data_category.js | 98 +++++-------------- .../view/blockly/overrides/field_dropdown.js | 24 ++--- .../view/blockly/overrides/field_image.js | 52 +++------- .../view/blockly/overrides/flyout_base.js | 7 +- .../view/blockly/overrides/flyout_vertical.js | 59 ++++++----- src/botPage/view/blockly/overrides/icon.js | 18 +++- .../view/blockly/overrides/procedures.js | 39 ++++---- src/botPage/view/blockly/overrides/toolbox.js | 31 ++---- .../view/blockly/overrides/variables.js | 23 +++-- 12 files changed, 149 insertions(+), 214 deletions(-) diff --git a/src/botPage/view/blockly/overrides/block.js b/src/botPage/view/blockly/overrides/block.js index 4af0628b66..b95986caba 100644 --- a/src/botPage/view/blockly/overrides/block.js +++ b/src/botPage/view/blockly/overrides/block.js @@ -1,3 +1,5 @@ +/* eslint-disable func-names */ + Blockly.Block.prototype.getSiblings = function() { const siblings = [this]; ['getPreviousBlock', 'getNextBlock'].forEach(functionName => { diff --git a/src/botPage/view/blockly/overrides/block_svg.js b/src/botPage/view/blockly/overrides/block_svg.js index 805531535d..8172231dc2 100644 --- a/src/botPage/view/blockly/overrides/block_svg.js +++ b/src/botPage/view/blockly/overrides/block_svg.js @@ -1,9 +1,10 @@ +/* eslint-disable func-names, no-underscore-dangle */ /** * Set whether the block is disabled or not. * @param {boolean} disabled True if disabled. */ Blockly.BlockSvg.prototype.setDisabled = function(disabled) { - if (this.disabled != disabled) { + if (this.disabled !== disabled) { Blockly.BlockSvg.superClass_.setDisabled.call(this, disabled); if (this.rendered) { this.updateDisabled(); @@ -27,7 +28,7 @@ Blockly.BlockSvg.prototype.updateDisabled = function() { } } const children = this.getChildren(false); - for (var i = 0, child; (child = children[i]); i++) { + children.forEach(child => { child.updateDisabled(); - } + }); }; diff --git a/src/botPage/view/blockly/overrides/contextmenu.js b/src/botPage/view/blockly/overrides/contextmenu.js index a8750bfae1..c5b090cd38 100644 --- a/src/botPage/view/blockly/overrides/contextmenu.js +++ b/src/botPage/view/blockly/overrides/contextmenu.js @@ -1,3 +1,4 @@ +// eslint-disable-next-line func-names Blockly.ContextMenu.blockDeleteOption = function(block) { // Option to delete this block but not blocks lower in the stack. // Count the number of blocks that are nested in this block, @@ -10,7 +11,7 @@ Blockly.ContextMenu.blockDeleteOption = function(block) { } const deleteOption = { text: - descendantCount == 1 + descendantCount === 1 ? Blockly.Msg.DELETE_BLOCK : Blockly.Msg.DELETE_X_BLOCKS.replace('%1', String(descendantCount)), enabled: true, diff --git a/src/botPage/view/blockly/overrides/data_category.js b/src/botPage/view/blockly/overrides/data_category.js index eebc8efb42..72c61bede2 100644 --- a/src/botPage/view/blockly/overrides/data_category.js +++ b/src/botPage/view/blockly/overrides/data_category.js @@ -1,41 +1,6 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2017 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 Data Flyout components including variable and list blocks. - * @author marisaleung@google.com (Marisa Leung) - */ - +/* eslint-disable func-names, no-underscore-dangle */ import { translate } from '../../../../common/i18n'; -/** - * @name Blockly.DataCategory - * @namespace - * */ -goog.provide('Blockly.DataCategory'); - -goog.require('Blockly.Blocks'); -goog.require('Blockly.VariableModel'); -goog.require('Blockly.Variables'); -goog.require('Blockly.Workspace'); - /** * Construct the blocks required by the flyout for the variable category. * @param {!Blockly.Workspace} workspace The workspace containing variables. @@ -45,8 +10,8 @@ Blockly.DataCategory = function(workspace) { const variableModelList = workspace.getVariablesOfType(''); const xmlList = []; - // Create Variable - Blockly.DataCategory.addCreateButton(xmlList, workspace, 'VARIABLE'); + // `Create Variable`-button + Blockly.DataCategory.addCreateButton(xmlList, workspace); if (variableModelList.length > 0) { const generateVariableFieldXmlString = variableModel => { @@ -58,7 +23,7 @@ Blockly.DataCategory = function(workspace) { return escapedText; }; - // TEMP + // TEMP: Label for testing only const operationsLabel = document.createElement('label'); operationsLabel.setAttribute('text', translate('variables_set')); xmlList.push(operationsLabel); @@ -68,15 +33,14 @@ Blockly.DataCategory = function(workspace) { // Create 'Set `var` to'-block if (Blockly.Blocks.variables_set) { const gap = Blockly.Blocks.math_change ? 8 : 24; - const setBlockText = - `${'' + ``}${generateVariableFieldXmlString( - firstVariable - )}` + ''; + const setBlockText = `${generateVariableFieldXmlString( + firstVariable + )}`; const setBlock = Blockly.Xml.textToDom(setBlockText).firstChild; xmlList.push(setBlock); } - // TEMP + // TEMP: Label for testing only const changeLabel = document.createElement('label'); changeLabel.setAttribute('text', translate('math_change')); xmlList.push(changeLabel); @@ -84,36 +48,29 @@ Blockly.DataCategory = function(workspace) { // Create 'Change `var` by `1`'-block if (Blockly.Blocks.math_change) { const gap = Blockly.Blocks.variables_get ? 20 : 8; - const changeBlockText = - `${'' + ``}${generateVariableFieldXmlString( - firstVariable - )}` + - '' + - '1' + - '' + - '' + - '' + - ''; + const changeBlockText = `${generateVariableFieldXmlString( + firstVariable + )}1`; const changeBlock = Blockly.Xml.textToDom(changeBlockText).firstChild; xmlList.push(changeBlock); } + // TEMP: Label for testing only const variablesLabel = document.createElement('label'); variablesLabel.setAttribute('text', translate('variable_get')); xmlList.push(variablesLabel); - // Create variable_get block for each variable + // Create `variable_get` block for each variable if (Blockly.Blocks.variables_get) { variableModelList.sort(Blockly.VariableModel.compareByName); - for (var i = 0, variable; (variable = variableModelList[i]); i++) { - const getBlockText = - `${'' + ''}${generateVariableFieldXmlString(variable)}` + - ''; - + variableModelList.forEach(variable => { + const getBlockText = `${generateVariableFieldXmlString( + variable + )}`; const getBlock = Blockly.Xml.textToDom(getBlockText).firstChild; xmlList.push(getBlock); - } + }); } } @@ -124,22 +81,21 @@ Blockly.DataCategory = function(workspace) { * Construct a create variable button and push it to the xmlList. * @param {!Array.} xmlList Array of XML block elements. * @param {Blockly.Workspace} workspace Workspace to register callback to. - * @param {string} type Type of variable this is for. For example, 'LIST' or - * 'VARIABLE'. + * binary-bot: We only use a single type of variable, so `type` arg was removed. */ -Blockly.DataCategory.addCreateButton = function(xmlList, workspace, type) { - const button = goog.dom.createDom('button'); +Blockly.DataCategory.addCreateButton = function(xmlList, workspace) { + const buttonXml = goog.dom.createDom('button'); // Set default msg, callbackKey, and callback values for type 'VARIABLE' const msg = Blockly.Msg.NEW_VARIABLE; const callbackKey = 'CREATE_VARIABLE'; const callback = function(button) { - const workspace = button.getTargetWorkspace(); - Blockly.Variables.createVariable(workspace, null, ''); - workspace.toolbox_.showCategory_('Variables'); + const buttonWorkspace = button.getTargetWorkspace(); + Blockly.Variables.createVariable(buttonWorkspace, null, ''); + buttonWorkspace.toolbox_.showCategory_('Variables'); }; - button.setAttribute('text', msg); - button.setAttribute('callbackKey', callbackKey); + buttonXml.setAttribute('text', msg); + buttonXml.setAttribute('callbackKey', callbackKey); workspace.registerButtonCallback(callbackKey, callback); - xmlList.push(button); + xmlList.push(buttonXml); }; diff --git a/src/botPage/view/blockly/overrides/field_dropdown.js b/src/botPage/view/blockly/overrides/field_dropdown.js index bb1625a469..6868170eec 100644 --- a/src/botPage/view/blockly/overrides/field_dropdown.js +++ b/src/botPage/view/blockly/overrides/field_dropdown.js @@ -1,21 +1,21 @@ -import { hideEventsFromBlockly } from '../utils'; - -Blockly.FieldDropdown.prototype.updateOptions = function(options, opt_default = null, triggerEvent = true) { +/* eslint-disable func-names, no-underscore-dangle */ +Blockly.FieldDropdown.prototype.updateOptions = function(options, optDefault = null, triggerEvent = true) { const previousValue = this.getValue(); + Blockly.Events.disable(); + this.menuGenerator_ = options; - if (opt_default && this.menuGenerator_.findIndex(item => item[1] === opt_default) !== -1) { - hideEventsFromBlockly(() => { - this.setValue(''); - this.setValue(opt_default); - }); + + if (optDefault && this.menuGenerator_.findIndex(item => item[1] === optDefault) !== -1) { + this.setValue(''); + this.setValue(optDefault); } else if (this.menuGenerator_.length > 0) { - hideEventsFromBlockly(() => { - this.setValue(''); - this.setValue(this.menuGenerator_[0][1]); - }); + this.setValue(''); + this.setValue(this.menuGenerator_[0][1]); } + Blockly.Events.enable(); + if (triggerEvent) { const event = new Blockly.Events.BlockChange( this.sourceBlock_, diff --git a/src/botPage/view/blockly/overrides/field_image.js b/src/botPage/view/blockly/overrides/field_image.js index 564e91ab44..c6f0fa4b63 100644 --- a/src/botPage/view/blockly/overrides/field_image.js +++ b/src/botPage/view/blockly/overrides/field_image.js @@ -1,61 +1,31 @@ -/** - * @license - * Visual Blocks Editor - * - * Copyright 2012 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 Image field. Used for pictures, icons, etc. - * @author fraser@google.com (Neil Fraser) - */ - -goog.provide('Blockly.FieldImage'); - -goog.require('Blockly.Field'); -goog.require('Blockly.utils'); - -goog.require('goog.math.Size'); - +/* eslint-disable func-names, no-underscore-dangle */ /** * Class for an image on a block. + * binary-bot: Blockly implementation vs. Scratch for click handlers * @param {string} src The URL of the image. * @param {number} width Width of the image. * @param {number} height Height of the image. - * @param {string=} opt_alt Optional alt text for when block is collapsed. - * @param {Function=} opt_onClick Optional function to be called when the image - * is clicked. If opt_onClick is defined, opt_alt must also be defined. - * @param {boolean=} opt_flipRtl Whether to flip the icon in RTL. + * @param {string=} optAlt Optional alt text for when block is collapsed. + * @param {Function=} optOnClick Optional function to be called when the image + * is clicked. If optOnClick is defined, optAlt must also be defined. + * @param {boolean=} optFlipRtl Whether to flip the icon in RTL. * @extends {Blockly.Field} * @constructor */ -Blockly.FieldImage = function(src, width, height, opt_alt, opt_onClick, opt_flipRtl) { +Blockly.FieldImage = function(src, width, height, optAlt, optOnClick, optFlipRtl) { this.sourceBlock_ = null; // Ensure height and width are numbers. Strings are bad at math. this.height_ = Number(height); this.width_ = Number(width); this.size_ = new goog.math.Size(this.width_, this.height_); - this.flipRtl_ = opt_flipRtl; + this.flipRtl_ = optFlipRtl; this.tooltip_ = ''; this.setValue(src); - this.setText(opt_alt); + this.setText(optAlt); - if (typeof opt_onClick === 'function') { - this.clickHandler_ = opt_onClick; + if (typeof optOnClick === 'function') { + this.clickHandler_ = optOnClick; } }; goog.inherits(Blockly.FieldImage, Blockly.Field); diff --git a/src/botPage/view/blockly/overrides/flyout_base.js b/src/botPage/view/blockly/overrides/flyout_base.js index 43c243b768..d91281b208 100644 --- a/src/botPage/view/blockly/overrides/flyout_base.js +++ b/src/botPage/view/blockly/overrides/flyout_base.js @@ -1,3 +1,4 @@ +/* eslint-disable func-names, no-underscore-dangle */ /** * Margin around the edges of the blocks in the flyout. * @type {number} @@ -17,13 +18,13 @@ Blockly.Flyout.prototype.MARGIN = 24; Blockly.Flyout.prototype.positionAt_ = function(width, height, x, y) { this.svgGroup_.setAttribute('width', width); this.svgGroup_.setAttribute('height', height); - if (this.svgGroup_.tagName == 'svg') { - var transform = `translate(${x}px,${y}px)`; + if (this.svgGroup_.tagName === 'svg') { + const transform = `translate(${x}px,${y}px)`; Blockly.utils.setCssTransform(this.svgGroup_, transform); } else { // IE and Edge don't support CSS transforms on SVG elements so // it's important to set the transform on the SVG element itself - var transform = `translate(${x},${y})`; + const transform = `translate(${x},${y})`; this.svgGroup_.setAttribute('transform', transform); } diff --git a/src/botPage/view/blockly/overrides/flyout_vertical.js b/src/botPage/view/blockly/overrides/flyout_vertical.js index dc284b6720..1a26dc19a7 100644 --- a/src/botPage/view/blockly/overrides/flyout_vertical.js +++ b/src/botPage/view/blockly/overrides/flyout_vertical.js @@ -1,3 +1,7 @@ +/* eslint-disable func-names, no-underscore-dangle */ +// binary-bot: Blockly value, Scratch resets this to, req for correct spacing in flyout. +Blockly.BlockSvg.TAB_WIDTH = 8; + /** * Move the flyout to the edge of the workspace. */ @@ -22,28 +26,30 @@ Blockly.VerticalFlyout.prototype.position = function() { // Y is always 0 since this is a vertical flyout. // binary-bot: We want some spacing between top and toolbox. const y = 10; + let x; + // If this flyout is the toolbox flyout. - if (this.targetWorkspace_.toolboxPosition == this.toolboxPosition_) { + if (this.targetWorkspace_.toolboxPosition === this.toolboxPosition_) { // If there is a category toolbox. if (targetWorkspaceMetrics.toolboxWidth) { - if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { - var x = targetWorkspaceMetrics.toolboxWidth - 100; // binary-bot: Dirty spacing, but it works. TODO, calc properly. + if (this.toolboxPosition_ === Blockly.TOOLBOX_AT_LEFT) { + x = targetWorkspaceMetrics.toolboxWidth - 100; // binary-bot: Dirty spacing, but it works. TODO, calc properly. } else { - var x = targetWorkspaceMetrics.viewWidth - this.width_; + x = targetWorkspaceMetrics.viewWidth - this.width_; } - } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { - var x = 0; + } else if (this.toolboxPosition_ === Blockly.TOOLBOX_AT_LEFT) { + x = 0; } else { - var x = targetWorkspaceMetrics.viewWidth; + x = targetWorkspaceMetrics.viewWidth; } - } else if (this.toolboxPosition_ == Blockly.TOOLBOX_AT_LEFT) { - var x = 0; + } else if (this.toolboxPosition_ === Blockly.TOOLBOX_AT_LEFT) { + x = 0; } else { // Because the anchor point of the flyout is on the left, but we want // to align the right edge of the flyout with the right edge of the // blocklyDiv, we calculate the full width of the div minus the width // of the flyout. - var x = targetWorkspaceMetrics.viewWidth + targetWorkspaceMetrics.absoluteLeft - this.width_; + x = targetWorkspaceMetrics.viewWidth + targetWorkspaceMetrics.absoluteLeft - this.width_; } this.positionAt_(this.width_, this.height_, x, y); }; @@ -58,39 +64,40 @@ Blockly.VerticalFlyout.prototype.reflowInternal_ = function() { this.workspace_.scale = this.targetWorkspace_.scale; let flyoutWidth = 0; const blocks = this.workspace_.getTopBlocks(false); - for (var i = 0, block; (block = blocks[i]); i++) { - let width = block.getHeightWidth().width; + + blocks.forEach(block => { + let { width } = block.getHeightWidth(); if (block.outputConnection) { - width -= 8; // binary-bot: 8 is the original Blockly.BlockSvg.TAB_WIDTH + width -= Blockly.BlockSvg.TAB_WIDTH; } flyoutWidth = Math.max(flyoutWidth, width); - } - for (var i = 0, button; (button = this.buttons_[i]); i++) { + }); + + this.buttons_.forEach(button => { flyoutWidth = Math.max(flyoutWidth, button.width); - } - flyoutWidth += this.MARGIN * 1.5 + 8; // binary-bot: 8 is the original Blockly.BlockSvg.TAB_WIDTH + }); + + flyoutWidth += this.MARGIN * 1.5 + Blockly.BlockSvg.TAB_WIDTH; flyoutWidth *= this.workspace_.scale; flyoutWidth += Blockly.Scrollbar.scrollbarThickness; - if (this.width_ != flyoutWidth) { - for (var i = 0, block; (block = blocks[i]); i++) { + if (this.width_ !== flyoutWidth) { + blocks.forEach(block => { if (this.RTL) { // With the flyoutWidth known, right-align the blocks. const oldX = block.getRelativeToSurfaceXY().x; const newX = flyoutWidth / this.workspace_.scale - this.MARGIN - Blockly.BlockSvg.TAB_WIDTH; block.moveBy(newX - oldX, 0); } - // if (block.flyoutRect_) { - // this.moveRectToBlock_(block.flyoutRect_, block); - // } - } + }); + if (this.RTL) { // With the flyoutWidth known, right-align the buttons. - for (var i = 0, button; (button = this.buttons_[i]); i++) { - const y = button.getPosition().y; + this.buttons_.forEach(button => { + const { y } = button.getPosition(); const x = flyoutWidth / this.workspace_.scale - button.width - this.MARGIN - Blockly.BlockSvg.TAB_WIDTH; button.moveTo(x, y); - } + }); } // Record the width for .getMetrics_ and .position. this.width_ = flyoutWidth; diff --git a/src/botPage/view/blockly/overrides/icon.js b/src/botPage/view/blockly/overrides/icon.js index f7063d22fe..e4f09229df 100644 --- a/src/botPage/view/blockly/overrides/icon.js +++ b/src/botPage/view/blockly/overrides/icon.js @@ -1,3 +1,4 @@ +/* eslint-disable func-names, no-underscore-dangle */ /** * Render the icon. * @param {number} cursorX Horizontal offset at which to position the icon. @@ -8,19 +9,26 @@ Blockly.Icon.prototype.renderIcon = function(cursorX) { this.iconGroup_.setAttribute('display', 'none'); return cursorX; } + this.iconGroup_.setAttribute('display', 'block'); + let newCursorX = cursorX; + const TOP_MARGIN = 9; const width = this.SIZE; + if (this.block_.RTL) { - cursorX -= width; + newCursorX -= width; } - this.iconGroup_.setAttribute('transform', `translate(${cursorX},${TOP_MARGIN})`); + + this.iconGroup_.setAttribute('transform', `translate(${newCursorX},${TOP_MARGIN})`); this.computeIconLocation(); + if (this.block_.RTL) { - cursorX -= Blockly.BlockSvg.SEP_SPACE_X; + newCursorX -= Blockly.BlockSvg.SEP_SPACE_X; } else { - cursorX += width + Blockly.BlockSvg.SEP_SPACE_X; + newCursorX += width + Blockly.BlockSvg.SEP_SPACE_X; } - return cursorX; + + return newCursorX; }; diff --git a/src/botPage/view/blockly/overrides/procedures.js b/src/botPage/view/blockly/overrides/procedures.js index 4cad6b8d0d..eda3eb9c50 100644 --- a/src/botPage/view/blockly/overrides/procedures.js +++ b/src/botPage/view/blockly/overrides/procedures.js @@ -1,3 +1,4 @@ +/* eslint-disable func-names, no-underscore-dangle */ import { translate } from '../../../../common/i18n'; /** @@ -16,12 +17,12 @@ Blockly.Procedures.flyoutCategory = function(workspace) { // // do something // - var block = document.createElement('block'); + const block = document.createElement('block'); block.setAttribute('type', 'procedures_defnoreturn'); block.setAttribute('gap', 16); // TEMP - var nameField = document.createElement('field'); + const nameField = document.createElement('field'); nameField.setAttribute('name', 'NAME'); nameField.appendChild(document.createTextNode(translate('do something'))); @@ -38,12 +39,14 @@ Blockly.Procedures.flyoutCategory = function(workspace) { // // do something // - var block = document.createElement('block'); + const block = document.createElement('block'); block.setAttribute('type', 'procedures_defreturn'); block.setAttribute('gap', 16); - var nameField = document.createElement('field'); + + const nameField = document.createElement('field'); nameField.setAttribute('name', 'NAME'); nameField.appendChild(document.createTextNode(translate('do something'))); + block.appendChild(nameField); xmlList.push(block); } @@ -54,7 +57,7 @@ Blockly.Procedures.flyoutCategory = function(workspace) { xmlList.push(label); // - var block = document.createElement('block'); + const block = document.createElement('block'); block.setAttribute('type', 'procedures_ifreturn'); block.setAttribute('gap', 16); xmlList.push(block); @@ -69,6 +72,7 @@ Blockly.Procedures.flyoutCategory = function(workspace) { for (let i = 0; i < procedureList.length; i++) { const name = procedureList[i][0]; const args = procedureList[i][1]; + // // // @@ -82,11 +86,11 @@ Blockly.Procedures.flyoutCategory = function(workspace) { mutation.setAttribute('name', name); block.appendChild(mutation); - for (let j = 0; j < args.length; j++) { + args.forEach(argumentName => { const arg = document.createElement('arg'); - arg.setAttribute('name', args[j]); + arg.setAttribute('name', argumentName); mutation.appendChild(arg); - } + }); xmlList.push(block); } @@ -120,19 +124,14 @@ Blockly.Procedures.getDefinition = function(name, workspace) { // Scratch has a broken version where they return `false` if Blockly.Names.equals(procName[0], name). // https://github.com/LLK/scratch-blocks/pull/1930 -Blockly.Procedures.isNameUsed = function(name, workspace, opt_exclude) { +Blockly.Procedures.isNameUsed = function(name, workspace, optExclude) { const blocks = workspace.getAllBlocks(false); // Iterate through every block and check the name. - for (let i = 0; i < blocks.length; i++) { - if (blocks[i] == opt_exclude) { - continue; + return blocks.some(block => { + if (block !== optExclude && block.getProcedureDef) { + const procName = block.getProcedureDef(); + return Blockly.Names.equals(procName[0], name); } - if (blocks[i].getProcedureDef) { - const procName = blocks[i].getProcedureDef(); - if (Blockly.Names.equals(procName[0], name)) { - return true; - } - } - } - return false; + return false; + }); }; diff --git a/src/botPage/view/blockly/overrides/toolbox.js b/src/botPage/view/blockly/overrides/toolbox.js index 7cea3b68c0..af6ee74097 100644 --- a/src/botPage/view/blockly/overrides/toolbox.js +++ b/src/botPage/view/blockly/overrides/toolbox.js @@ -1,3 +1,4 @@ +/* eslint-disable func-names, no-underscore-dangle */ // /** // * Fill the toolbox with categories and blocks. // * @param {!Node} newTree DOM tree of blocks. @@ -15,36 +16,23 @@ Blockly.Toolbox.prototype.populate_ = function(newTree) { Blockly.Toolbox.prototype.showCategory_ = function(categoryName) { let allContents = []; - const category = this.categoryMenu_.categories_.find(category => category.name_ === categoryName); + const category = this.categoryMenu_.categories_.find(menuCategory => menuCategory.name_ === categoryName); if (!category) { return; } - // const labelString = '' + - // `' + - // ''; - - // const labelXML = Blockly.Xml.textToDom(labelString); - // allContents.push(labelXML.firstChild); allContents = allContents.concat(category.getContents()); - // TESTING ONLY: Generate labels for each block for QA + // TEMP: For testing only, generate labels for each block for QA let newAllContents = []; if (Array.isArray(allContents) && allContents.length > 1) { - allContents.forEach((node, index) => { + allContents.forEach(node => { if (node.nodeName === 'block') { const type = node.getAttribute('type'); - - const labelString = - '' + - `' + - ''; + const labelString = ``; const labelXml = Blockly.Xml.textToDom(labelString); - newAllContents.push(...[labelXml.firstChild, node]); } }); @@ -59,11 +47,10 @@ Blockly.Toolbox.prototype.showCategory_ = function(categoryName) { /** * Opens the selected category - * binary-bot: unlike in Scratch, we want to have category-specific flyouts. + * binary-bot: unlike in Scratch, we want to have category-specific flyouts + removed opt_shouldScroll * @param {Blockly.Toolbox.Category} item The category to select. - * @param {boolean=} opt_shouldScroll Whether to scroll to the selected category. Unused in Binary Bot. */ -Blockly.Toolbox.prototype.setSelectedItem = function(item, opt_shouldScroll) { +Blockly.Toolbox.prototype.setSelectedItem = function(item) { if (this.selectedItem_) { // They selected a different category but one was already open. Close it. this.selectedItem_.setSelected(false); @@ -107,7 +94,7 @@ Blockly.Toolbox.prototype.position = function() { treeDiv.style.height = 'auto'; treeDiv.style.width = `${svgSize.width}px`; this.height = treeDiv.offsetHeight; - if (this.toolboxPosition == Blockly.TOOLBOX_AT_TOP) { + if (this.toolboxPosition === Blockly.TOOLBOX_AT_TOP) { // Top treeDiv.style.top = '0'; } else { @@ -115,7 +102,7 @@ Blockly.Toolbox.prototype.position = function() { treeDiv.style.bottom = '0'; } } else { - if (this.toolboxPosition == Blockly.TOOLBOX_AT_RIGHT) { + if (this.toolboxPosition === Blockly.TOOLBOX_AT_RIGHT) { // Right treeDiv.style.right = '0'; } else { diff --git a/src/botPage/view/blockly/overrides/variables.js b/src/botPage/view/blockly/overrides/variables.js index 8edf1dbca3..f96802d5cc 100644 --- a/src/botPage/view/blockly/overrides/variables.js +++ b/src/botPage/view/blockly/overrides/variables.js @@ -1,3 +1,4 @@ +/* eslint-disable func-names */ /** * Find all user-created variables that are in use in the workspace. * For use by generators. @@ -10,23 +11,25 @@ Blockly.Variables.allUsedVarModels = function(ws) { const blocks = ws.getAllBlocks(false); const variableHash = Object.create(null); + // Iterate through every block and add each variable to the hash. - for (let i = 0; i < blocks.length; i++) { - const blockVariables = blocks[i].getVarModels(); + blocks.forEach(block => { + const blockVariables = block.getVarModels(); if (blockVariables) { - for (let j = 0; j < blockVariables.length; j++) { - const variable = blockVariables[j]; - var id = variable.getId(); + blockVariables.forEach(blockVariable => { + const id = blockVariable.getId(); if (id) { - variableHash[id] = variable; + variableHash[id] = blockVariable; } - } + }); } - } + }); + // Flatten the hash into a list. const variableList = []; - for (var id in variableHash) { + Object.keys(variableHash).forEach(id => { variableList.push(variableHash[id]); - } + }); + return variableList; }; From 544c222d4376605b904e7dc6682f539ba8398fc1 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 11:55:13 +0800 Subject: [PATCH 16/74] do disableStrayBlocks(), consider as main block --- src/botPage/common/const.js | 2 +- src/botPage/view/blockly/index.js | 4 +--- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/botPage/common/const.js b/src/botPage/common/const.js index 540e63a37c..0dc4bb1c16 100644 --- a/src/botPage/common/const.js +++ b/src/botPage/common/const.js @@ -173,7 +173,7 @@ const config = { [translate('8 hours'), '28800'], [translate('1 day'), '86400'], ], - mainBlocks : ['trade', 'before_purchase', 'after_purchase', 'during_purchase'], + mainBlocks : ['trade_definition', 'before_purchase', 'after_purchase', 'during_purchase'], conditionsCategory: { callput : ['risefall', 'higherlower'], callputequal: ['risefallequals'], diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index e27a5ce314..a7352c4f39 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -24,7 +24,6 @@ import { observer as globalObserver } from '../../../common/utils/observer'; import { showDialog } from '../../bot/tools'; const setBeforeUnload = off => { - return; if (off) { window.onbeforeunload = null; } else { @@ -174,7 +173,6 @@ const render = workspace => () => { const overrideBlocklyDefaultShape = () => { const addDownloadToMenu = block => { if (block instanceof Object) { - return; // eslint-disable-next-line no-param-reassign, max-len block.customContextMenu = function customContextMenu(options) { options.push({ @@ -329,7 +327,7 @@ export default class _Blockly { save(filename, collection, xml); } run(limitations = {}) { - // disableStrayBlocks(); + disableStrayBlocks(); const workspaceCode = Blockly.JavaScript.workspaceToCode(Blockly.mainWorkspace); From 716186d570d8b607b765ff2144d353e05839ff93 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 11:55:31 +0800 Subject: [PATCH 17/74] Add as global variable --- .eslintrc | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.eslintrc b/.eslintrc index 582942b284..609e40c461 100644 --- a/.eslintrc +++ b/.eslintrc @@ -19,7 +19,8 @@ "globals": { "Blockly": false, "trackJs": false, - "jest": false + "jest": false, + "goog": false }, "plugins": [ "react" From d81d4b27f4a2274810ed8a230678253d9c7dc7fa Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 11:55:49 +0800 Subject: [PATCH 18/74] Update package.json & package-lock.json --- package-lock.json | 124 +++++++++++++++++++++++----------------------- package.json | 2 +- 2 files changed, 63 insertions(+), 63 deletions(-) diff --git a/package-lock.json b/package-lock.json index f905f35af6..b4b2ca0766 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7988,28 +7988,28 @@ "dependencies": { "abbrev": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "resolved": false, "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true, "optional": true }, "ansi-regex": { "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "resolved": false, "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=", "dev": true, "optional": true }, "aproba": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz", + "resolved": false, "integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw==", "dev": true, "optional": true }, "are-we-there-yet": { "version": "1.1.5", - "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz", + "resolved": false, "integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==", "dev": true, "optional": true, @@ -8020,14 +8020,14 @@ }, "balanced-match": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "resolved": false, "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=", "dev": true, "optional": true }, "brace-expansion": { "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "resolved": false, "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, "optional": true, @@ -8038,35 +8038,35 @@ }, "chownr": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.1.tgz", + "resolved": false, "integrity": "sha512-j38EvO5+LHX84jlo6h4UzmOwi0UgW61WRyPtJz4qaadK5eY3BTS5TY/S1Stc3Uk2lIM6TPevAlULiEJwie860g==", "dev": true, "optional": true }, "code-point-at": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", + "resolved": false, "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=", "dev": true, "optional": true }, "concat-map": { "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "resolved": false, "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true, "optional": true }, "console-control-strings": { "version": "1.1.0", - "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "resolved": false, "integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4=", "dev": true, "optional": true }, "core-util-is": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=", "dev": true, "optional": true @@ -8083,28 +8083,28 @@ }, "deep-extend": { "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "resolved": false, "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", "dev": true, "optional": true }, "delegates": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "resolved": false, "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o=", "dev": true, "optional": true }, "detect-libc": { "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", + "resolved": false, "integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups=", "dev": true, "optional": true }, "fs-minipass": { "version": "1.2.5", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.5.tgz", + "resolved": false, "integrity": "sha512-JhBl0skXjUPCFH7x6x61gQxrKyXsxB5gcgePLZCwfyCGGsTISMoIeObbrvVeP6Xmyaudw4TT43qV2Gz+iyd2oQ==", "dev": true, "optional": true, @@ -8114,14 +8114,14 @@ }, "fs.realpath": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "resolved": false, "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true, "optional": true }, "gauge": { "version": "2.7.4", - "resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz", + "resolved": false, "integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=", "dev": true, "optional": true, @@ -8138,7 +8138,7 @@ }, "glob": { "version": "7.1.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.3.tgz", + "resolved": false, "integrity": "sha512-vcfuiIxogLV4DlGBHIUOwI0IbrJ8HWPc4MU7HzviGeNho/UJDfi6B5p3sHeWIQ0KGIU0Jpxi5ZHxemQfLkkAwQ==", "dev": true, "optional": true, @@ -8153,14 +8153,14 @@ }, "has-unicode": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "resolved": false, "integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk=", "dev": true, "optional": true }, "iconv-lite": { "version": "0.4.24", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "resolved": false, "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", "dev": true, "optional": true, @@ -8170,7 +8170,7 @@ }, "ignore-walk": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.1.tgz", + "resolved": false, "integrity": "sha512-DTVlMx3IYPe0/JJcYP7Gxg7ttZZu3IInhuEhbchuqneY9wWe5Ojy2mXLBaQFUQmo0AW2r3qG7m1mg86js+gnlQ==", "dev": true, "optional": true, @@ -8180,7 +8180,7 @@ }, "inflight": { "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "resolved": false, "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", "dev": true, "optional": true, @@ -8191,21 +8191,21 @@ }, "inherits": { "version": "2.0.3", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "resolved": false, "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=", "dev": true, "optional": true }, "ini": { "version": "1.3.5", - "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz", + "resolved": false, "integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw==", "dev": true, "optional": true }, "is-fullwidth-code-point": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", + "resolved": false, "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=", "dev": true, "optional": true, @@ -8215,14 +8215,14 @@ }, "isarray": { "version": "1.0.0", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "resolved": false, "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true, "optional": true }, "minimatch": { "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "resolved": false, "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, "optional": true, @@ -8232,14 +8232,14 @@ }, "minimist": { "version": "0.0.8", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": false, "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=", "dev": true, "optional": true }, "minipass": { "version": "2.3.5", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.3.5.tgz", + "resolved": false, "integrity": "sha512-Gi1W4k059gyRbyVUZQ4mEqLm0YIUiGYfvxhF6SIlk3ui1WVxMTGfGdQ2SInh3PDrRTVvPKgULkpJtT4RH10+VA==", "dev": true, "optional": true, @@ -8250,7 +8250,7 @@ }, "minizlib": { "version": "1.2.1", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.2.1.tgz", + "resolved": false, "integrity": "sha512-7+4oTUOWKg7AuL3vloEWekXY2/D20cevzsrNT2kGWm+39J9hGTCBv8VI5Pm5lXZ/o3/mdR4f8rflAPhnQb8mPA==", "dev": true, "optional": true, @@ -8260,7 +8260,7 @@ }, "mkdirp": { "version": "0.5.1", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz", + "resolved": false, "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=", "dev": true, "optional": true, @@ -8308,7 +8308,7 @@ }, "nopt": { "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", + "resolved": false, "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00=", "dev": true, "optional": true, @@ -8337,7 +8337,7 @@ }, "npmlog": { "version": "4.1.2", - "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz", + "resolved": false, "integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==", "dev": true, "optional": true, @@ -8350,21 +8350,21 @@ }, "number-is-nan": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", + "resolved": false, "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=", "dev": true, "optional": true }, "object-assign": { "version": "4.1.1", - "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "resolved": false, "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=", "dev": true, "optional": true }, "once": { "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "resolved": false, "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, "optional": true, @@ -8374,21 +8374,21 @@ }, "os-homedir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=", "dev": true, "optional": true }, "os-tmpdir": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "resolved": false, "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=", "dev": true, "optional": true }, "osenv": { "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", + "resolved": false, "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", "dev": true, "optional": true, @@ -8399,21 +8399,21 @@ }, "path-is-absolute": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "resolved": false, "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", "dev": true, "optional": true }, "process-nextick-args": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.0.tgz", + "resolved": false, "integrity": "sha512-MtEC1TqN0EU5nephaJ4rAtThHtC86dNN9qCuEhtshvpVBkAW5ZO7BASN9REnF9eoXGcRub+pFuKEpOHE+HbEMw==", "dev": true, "optional": true }, "rc": { "version": "1.2.8", - "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "resolved": false, "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "optional": true, @@ -8426,7 +8426,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": false, "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true, "optional": true @@ -8435,7 +8435,7 @@ }, "readable-stream": { "version": "2.3.6", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.6.tgz", + "resolved": false, "integrity": "sha512-tQtKA9WIAhBF3+VLAseyMqZeBjW0AHJoxOtYqSUZNJxauErmLbVm2FW1y+J/YA9dUrAC39ITejlZWhVIwawkKw==", "dev": true, "optional": true, @@ -8451,7 +8451,7 @@ }, "rimraf": { "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "resolved": false, "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", "dev": true, "optional": true, @@ -8461,21 +8461,21 @@ }, "safe-buffer": { "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "resolved": false, "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true, "optional": true }, "safer-buffer": { "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "resolved": false, "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, "optional": true }, "sax": { "version": "1.2.4", - "resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz", + "resolved": false, "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true, "optional": true @@ -8489,21 +8489,21 @@ }, "set-blocking": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "resolved": false, "integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc=", "dev": true, "optional": true }, "signal-exit": { "version": "3.0.2", - "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.2.tgz", + "resolved": false, "integrity": "sha1-tf3AjxKH6hF4Yo5BXiUTK3NkbG0=", "dev": true, "optional": true }, "string-width": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", + "resolved": false, "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=", "dev": true, "optional": true, @@ -8515,7 +8515,7 @@ }, "string_decoder": { "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "resolved": false, "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, "optional": true, @@ -8525,7 +8525,7 @@ }, "strip-ansi": { "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "resolved": false, "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", "dev": true, "optional": true, @@ -8535,14 +8535,14 @@ }, "strip-json-comments": { "version": "2.0.1", - "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "resolved": false, "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo=", "dev": true, "optional": true }, "tar": { "version": "4.4.8", - "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.8.tgz", + "resolved": false, "integrity": "sha512-LzHF64s5chPQQS0IYBn9IN5h3i98c12bo4NCO7e0sGM2llXQ3p2FGC5sdENN4cTW48O915Sh+x+EXx7XW96xYQ==", "dev": true, "optional": true, @@ -8558,14 +8558,14 @@ }, "util-deprecate": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "resolved": false, "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true, "optional": true }, "wide-align": { "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", + "resolved": false, "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, "optional": true, @@ -8575,14 +8575,14 @@ }, "wrappy": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "resolved": false, "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true, "optional": true }, "yallist": { "version": "3.0.3", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.0.3.tgz", + "resolved": false, "integrity": "sha512-S+Zk8DEWE6oKpV+vI3qWkaK+jSbIK86pCwe2IF/xwIpQ8jEuxpw9NyaGjmp9+BoJv5FV2piqCDcoCtStppiq2A==", "dev": true, "optional": true @@ -14009,9 +14009,9 @@ } }, "jquery": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.2.1.tgz", - "integrity": "sha1-XE2d5lKvbNCncBVKYxu6ErAVx4c=", + "version": "3.4.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.4.1.tgz", + "integrity": "sha512-36+AdBzCL+y6qjw5Tx7HgzeGCzC81MDDgaUP8ld2zhx58HdqXGoBd+tHdrBMiyjGQs0Hxs/MLZTu/eHNJJuWPw==", "dev": true }, "jquery-mousewheel": { diff --git a/package.json b/package.json index 764e653960..e6a60d8688 100644 --- a/package.json +++ b/package.json @@ -76,7 +76,7 @@ "i18next-scanner": "1.9.4", "immutable": "^3.8.2", "jest": "^24.7.1", - "jquery": "3.2.1", + "jquery": "^3.4.0", "jquery-ui": "1.12.1", "jquery-ui-css": "1.11.4", "js-interpreter": "^1.4.6", From 2302feec3b88b26521329d46673560e0bff3855f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 13:06:29 +0800 Subject: [PATCH 19/74] Remove $.notify() debug messaging --- src/botPage/view/blockly/index.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index a7352c4f39..e881fd7495 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -236,10 +236,6 @@ export default class _Blockly { media : 'image/scratch/', }); - workspace.addChangeListener(event => { - $.notify(`Event: ${event.type}`); - }); - const renderInstance = render(workspace); window.addEventListener('resize', renderInstance, false); renderInstance(); From e3a03c61c22eedb62e79a894804a8c9bd6af8997 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 13:42:54 +0800 Subject: [PATCH 20/74] Remove unused eslint exceptions --- src/botPage/view/blockly/overrides/index.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/botPage/view/blockly/overrides/index.js b/src/botPage/view/blockly/overrides/index.js index 13bed964c7..0a04d85aaa 100644 --- a/src/botPage/view/blockly/overrides/index.js +++ b/src/botPage/view/blockly/overrides/index.js @@ -1,4 +1,3 @@ -/* eslint-disable */ import './block'; import './block_svg'; import './colours'; @@ -12,4 +11,3 @@ import './icon'; import './procedures'; import './toolbox'; import './variables'; -/* eslint-enable */ From 4235094b68769e9bd4d039831a740a1ef8356cff Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 13:43:14 +0800 Subject: [PATCH 21/74] Restore original Blockly contextMenu --- .../view/blockly/overrides/block_svg.js | 80 +++++++++++++++++++ 1 file changed, 80 insertions(+) diff --git a/src/botPage/view/blockly/overrides/block_svg.js b/src/botPage/view/blockly/overrides/block_svg.js index 8172231dc2..1d85c258cf 100644 --- a/src/botPage/view/blockly/overrides/block_svg.js +++ b/src/botPage/view/blockly/overrides/block_svg.js @@ -1,7 +1,10 @@ /* eslint-disable func-names, no-underscore-dangle */ +import { translate } from '../../../../common/i18n'; + /** * Set whether the block is disabled or not. * @param {boolean} disabled True if disabled. + * binary-bot: Call updateDisabled() when setDisabled is called. */ Blockly.BlockSvg.prototype.setDisabled = function(disabled) { if (this.disabled !== disabled) { @@ -32,3 +35,80 @@ Blockly.BlockSvg.prototype.updateDisabled = function() { child.updateDisabled(); }); }; + +/** + * Show the context menu for this block. + * @param {!Event} e Mouse event. + * @private + * binary-bot: Restore contextMenu options from Blockly unavailable in Scratch + */ +Blockly.BlockSvg.prototype.showContextMenu_ = function(e) { + if (this.workspace.options.readOnly || !this.contextMenu) { + return; + } + // Save the current block in a variable for use in closures. + const block = this; + const menuOptions = []; + + if (this.isDeletable() && this.isMovable() && !block.isInFlyout) { + menuOptions.push(Blockly.ContextMenu.blockDuplicateOption(block, e)); + + if (this.isEditable() && this.workspace.options.comments) { + menuOptions.push(Blockly.ContextMenu.blockCommentOption(block)); + } + + menuOptions.push(Blockly.ContextMenu.blockDeleteOption(block)); + } else if (this.parentBlock_ && this.isShadow_) { + this.parentBlock_.showContextMenu_(e); + return; + } + + // Option to collapse/expand block. + if (this.workspace.options.collapse) { + if (this.collapsed_) { + const expandOption = { enabled: true }; + expandOption.text = translate('Expand Block'); + expandOption.callback = function() { + block.setCollapsed(false); + }; + menuOptions.push(expandOption); + } else { + const collapseOption = { enabled: true }; + collapseOption.text = translate('Collapse Block'); + collapseOption.callback = function() { + block.setCollapsed(true); + }; + menuOptions.push(collapseOption); + } + } + + // Option to disable/enable block. + if (this.workspace.options.disable) { + const disableOption = { + text : this.disabled ? translate('Enable Block') : translate('Disable Block'), + enabled: !this.getInheritedDisabled(), + callback() { + const group = Blockly.Events.getGroup(); + + if (!group) { + Blockly.Events.setGroup(true); + } + + block.setDisabled(!block.disabled); + + if (!group) { + Blockly.Events.setGroup(false); + } + }, + }; + menuOptions.push(disableOption); + + // Allow the block to add or modify menuOptions. + if (this.customContextMenu) { + this.customContextMenu(menuOptions); + } + + Blockly.ContextMenu.show(e, menuOptions, this.RTL); + Blockly.ContextMenu.currentBlock = this; + } +}; From d942a0d4c4eb5214e94c309ce0a2c0636a665e4f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 13:47:19 +0800 Subject: [PATCH 22/74] Fix typo that would render incorrect block shape --- .../blocks/Scratch/Advanced/Loops/controls_for.js | 10 +++++----- .../blocks/Scratch/Advanced/Loops/controls_forEach.js | 10 +++++----- 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js index 525a6e89b0..720befd4fd 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_for.js @@ -33,11 +33,11 @@ Blockly.Blocks.controls_for = { name: 'DO', }, ], - colour : Blockly.Colours.Binary.colour, - colourSecondary : Blockly.Colours.Binary.colourSecondary, - colourTertiary : Blockly.Colours.Binary.colourTertiary, - previousStaement: null, - nextStatement : null, + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, }); }, }; diff --git a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js index 5232b2a255..2769fd94f9 100644 --- a/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js +++ b/src/botPage/view/blockly/blocks/Scratch/Advanced/Loops/controls_forEach.js @@ -23,11 +23,11 @@ Blockly.Blocks.controls_forEach = { name: 'DO', }, ], - colour : Blockly.Colours.Binary.colour, - colourSecondary : Blockly.Colours.Binary.colourSecondary, - colourTertiary : Blockly.Colours.Binary.colourTertiary, - previousStaement: null, - nextStatement : null, + colour : Blockly.Colours.Binary.colour, + colourSecondary : Blockly.Colours.Binary.colourSecondary, + colourTertiary : Blockly.Colours.Binary.colourTertiary, + previousStatement: null, + nextStatement : null, }); }, }; From 6018cc5e14661ecb4b35f0c35f685e62b233e3d0 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 7 May 2019 13:59:50 +0800 Subject: [PATCH 23/74] Set shadow block values to be consistent with Binary Bot --- static/xml/toolbox.xml | 137 +++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 33 deletions(-) diff --git a/static/xml/toolbox.xml b/static/xml/toolbox.xml index 71c7e0b491..8cc8ca4925 100644 --- a/static/xml/toolbox.xml +++ b/static/xml/toolbox.xml @@ -1,4 +1,3 @@ -