From 845ce97d42adf5588c2b776068516332e336cc04 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Thu, 30 May 2019 10:17:21 +0800 Subject: [PATCH 01/49] Remove sellExpired() --- src/botPage/bot/TradeEngine/OpenContract.js | 9 +-------- src/botPage/bot/TradeEngine/Sell.js | 5 ----- 2 files changed, 1 insertion(+), 13 deletions(-) diff --git a/src/botPage/bot/TradeEngine/OpenContract.js b/src/botPage/bot/TradeEngine/OpenContract.js index 424aebe810..a5d34ff6ab 100644 --- a/src/botPage/bot/TradeEngine/OpenContract.js +++ b/src/botPage/bot/TradeEngine/OpenContract.js @@ -17,8 +17,6 @@ export default Engine => this.setContractFlags(contract); - this.sellExpired(); - this.data = this.data.set('contract', contract); broadcastContract({ accountID: this.accountInfo.loginid, ...contract }); @@ -45,11 +43,7 @@ export default Engine => this.store.dispatch(openContractReceived()); if (!this.isExpired) { this.resetSubscriptionTimeout(); - return; - } - if (!this.retriedUnsuccessfullSellExpired) { - this.retriedUnsuccessfullSellExpired = true; - this.resetSubscriptionTimeout(AFTER_FINISH_TIMEOUT); + } } }); @@ -61,7 +55,6 @@ export default Engine => } subscribeToOpenContract(contractId = this.contractId) { if (this.contractId !== contractId) { - this.retriedUnsuccessfullSellExpired = false; this.resetSubscriptionTimeout(); } this.contractId = contractId; diff --git a/src/botPage/bot/TradeEngine/Sell.js b/src/botPage/bot/TradeEngine/Sell.js index 568fa6a15a..d099463fda 100644 --- a/src/botPage/bot/TradeEngine/Sell.js +++ b/src/botPage/bot/TradeEngine/Sell.js @@ -46,9 +46,4 @@ export default Engine => delayIndex++ ).then(onSuccess); } - sellExpired() { - if (this.isSellAvailable && this.isExpired) { - doUntilDone(() => this.api.sellExpiredContracts()); - } - } }; From 322c2aff540393c967e2f7712c4866b7ecd27ecf Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 4 Jun 2019 11:34:05 +0800 Subject: [PATCH 02/49] added 2 new blocks for date/time conversion --- .../view/blockly/blocks/tools/time/index.js | 2 ++ .../blockly/blocks/tools/time/todatetime.js | 25 ++++++++++++++++++ .../blockly/blocks/tools/time/totimestamp.js | 26 +++++++++++++++++++ static/xml/toolbox.xml | 14 ++++++++++ 4 files changed, 67 insertions(+) create mode 100644 src/botPage/view/blockly/blocks/tools/time/todatetime.js create mode 100644 src/botPage/view/blockly/blocks/tools/time/totimestamp.js diff --git a/src/botPage/view/blockly/blocks/tools/time/index.js b/src/botPage/view/blockly/blocks/tools/time/index.js index 2c41b91b5b..6dc16d8fa4 100644 --- a/src/botPage/view/blockly/blocks/tools/time/index.js +++ b/src/botPage/view/blockly/blocks/tools/time/index.js @@ -1,3 +1,5 @@ import './epoch'; import './timeout'; import './interval'; +import './todatetime'; +import './totimestamp'; diff --git a/src/botPage/view/blockly/blocks/tools/time/todatetime.js b/src/botPage/view/blockly/blocks/tools/time/todatetime.js new file mode 100644 index 0000000000..37063c5bcc --- /dev/null +++ b/src/botPage/view/blockly/blocks/tools/time/todatetime.js @@ -0,0 +1,25 @@ +// https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#wupwb4 +import { translate } from '../../../../../../common/i18n'; +// import { disable } from '../../../utils'; + +Blockly.Blocks.todatetime = { + init: function init() { + this.appendDummyInput(); + this.appendValueInput('TIMESTAMP').appendField(translate('To Date/Time')); + this.setInputsInline(true); + this.setOutput(true, 'String'); + this.setColour('#dedede'); + this.setTooltip( + translate( + 'Convert a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' + ) + ); + // this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); + }, +}; + +Blockly.JavaScript.todatetime = block => { + const timestamp = Blockly.JavaScript.valueToCode(block, 'TIMESTAMP', Blockly.JavaScript.ORDER_ATOMIC); + const code = `Bot.timestampToDateTimeString(${timestamp})`; + return [code, Blockly.JavaScript.ORDER_ATOMIC]; +}; diff --git a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js new file mode 100644 index 0000000000..028b9a03ea --- /dev/null +++ b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js @@ -0,0 +1,26 @@ +// https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#wupwb4 +import { translate } from '../../../../../../common/i18n'; +import { dateToTimeString } from 'binary-utils/lib'; +// import { disable } from '../../../utils'; + +Blockly.Blocks.totimestamp = { + init: function init() { + this.appendDummyInput(); + this.appendValueInput('DATETIME').appendField(translate('To Timestamp')); + this.setInputsInline(true); + this.setOutput(true, 'Number'); + this.setColour('#dedede'); + this.setTooltip( + translate( + 'Convert a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' + ) + ); + // this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); + }, +}; + +Blockly.JavaScript.totimestamp = block => { + const dString = Blockly.JavaScript.valueToCode(block, 'DATETIME', Blockly.JavaScript.ORDER_ATOMIC); + const code = `Bot.dateTimeStringToTimestamp('${dString}')`; + return [code, Blockly.JavaScript.ORDER_NONE]; +}; diff --git a/static/xml/toolbox.xml b/static/xml/toolbox.xml index d19db21264..776f5c8831 100644 --- a/static/xml/toolbox.xml +++ b/static/xml/toolbox.xml @@ -409,6 +409,20 @@ + + + + yyyy-mm-dd hh:mm:ss + + + + + + + 0 + + + From 4b35fb0a7868ac967534f4c905d1f002dd738e6c Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 4 Jun 2019 15:45:03 +0800 Subject: [PATCH 03/49] fix for time conversion blocks --- .../blockly/blocks/tools/time/todatetime.js | 32 +++++++++++++++++-- .../blockly/blocks/tools/time/totimestamp.js | 25 ++++++++++++--- 2 files changed, 49 insertions(+), 8 deletions(-) diff --git a/src/botPage/view/blockly/blocks/tools/time/todatetime.js b/src/botPage/view/blockly/blocks/tools/time/todatetime.js index 37063c5bcc..1c399dd6a8 100644 --- a/src/botPage/view/blockly/blocks/tools/time/todatetime.js +++ b/src/botPage/view/blockly/blocks/tools/time/todatetime.js @@ -11,7 +11,7 @@ Blockly.Blocks.todatetime = { this.setColour('#dedede'); this.setTooltip( translate( - 'Convert a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' + 'Converts a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' ) ); // this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); @@ -19,7 +19,33 @@ Blockly.Blocks.todatetime = { }; Blockly.JavaScript.todatetime = block => { + // const timestamp = Blockly.JavaScript.valueToCode(block, 'TIMESTAMP', Blockly.JavaScript.ORDER_ATOMIC); + // const code = `Bot.timestampToDateTimeString(${timestamp})`; + // return [code, Blockly.JavaScript.ORDER_ATOMIC]; + const timestamp = Blockly.JavaScript.valueToCode(block, 'TIMESTAMP', Blockly.JavaScript.ORDER_ATOMIC); - const code = `Bot.timestampToDateTimeString(${timestamp})`; - return [code, Blockly.JavaScript.ORDER_ATOMIC]; + + // eslint-disable-next-line no-underscore-dangle + const functionName = Blockly.JavaScript.provideFunction_('timestampToDateString', [ + // eslint-disable-next-line no-underscore-dangle + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(timestamp) { + var dateTime; + if(typeof timestamp == 'number') { + dateTime = new Date(timestamp * 1000); + } + return [ + dateTime.getFullYear().toString(), + dateTime.getMonth()+1 < 10 ? '0' + (dateTime.getMonth()+1) : dateTime.getMonth()+1, + dateTime.getDate() < 10 ? '0' + dateTime.getDate() : dateTime.getDate() + ].join('-') + ' ' + + [ + dateTime.getHours() < 10 ? '0' + dateTime.getHours() : dateTime.getHours(), + dateTime.getMinutes() < 10 ? '0' + dateTime.getMinutes() : dateTime.getMinutes(), + dateTime.getSeconds() < 10 ? '0' + dateTime.getSeconds() : dateTime.getSeconds() + ].join(':'); + }`, + ]); + + const code = `${functionName}(${timestamp})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; diff --git a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js index 028b9a03ea..e757e8f150 100644 --- a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js +++ b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js @@ -1,6 +1,6 @@ // https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#wupwb4 import { translate } from '../../../../../../common/i18n'; -import { dateToTimeString } from 'binary-utils/lib'; +// import { dateToTimeString } from 'binary-utils/lib'; // import { disable } from '../../../utils'; Blockly.Blocks.totimestamp = { @@ -12,7 +12,7 @@ Blockly.Blocks.totimestamp = { this.setColour('#dedede'); this.setTooltip( translate( - 'Convert a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' + 'Converts a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' ) ); // this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); @@ -20,7 +20,22 @@ Blockly.Blocks.totimestamp = { }; Blockly.JavaScript.totimestamp = block => { - const dString = Blockly.JavaScript.valueToCode(block, 'DATETIME', Blockly.JavaScript.ORDER_ATOMIC); - const code = `Bot.dateTimeStringToTimestamp('${dString}')`; - return [code, Blockly.JavaScript.ORDER_NONE]; + // const dString = Blockly.JavaScript.valueToCode(block, 'DATETIME', Blockly.JavaScript.ORDER_ATOMIC); + // const code = `Bot.dateTimeStringToTimestamp('${dString}')`; + // return [code, Blockly.JavaScript.ORDER_NONE]; + + // YYYY-MM-DD HH:MM:SS format + const dateString = Blockly.JavaScript.valueToCode(block, 'DATETIME', Blockly.JavaScript.ORDER_ATOMIC); + + // eslint-disable-next-line no-underscore-dangle + const functionName = Blockly.JavaScript.provideFunction_('dateTimeStringToTimestamp', [ + // eslint-disable-next-line no-underscore-dangle + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(dateTimeString) { + var date = new Date(dateTimeString); + return date ? date.getTime() / 1000 : 'Invalid date/time'; + }`, + ]); + + const code = `${functionName}(${dateString})`; + return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]; }; From 6c161f4f9695048be1551fd15d5deaf719398c40 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 11 Jun 2019 14:05:06 +0800 Subject: [PATCH 04/49] fix for error messages --- src/botPage/view/blockly/blocks/tools/time/todatetime.js | 9 ++++----- .../view/blockly/blocks/tools/time/totimestamp.js | 9 +++++++-- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/src/botPage/view/blockly/blocks/tools/time/todatetime.js b/src/botPage/view/blockly/blocks/tools/time/todatetime.js index 1c399dd6a8..0746aef765 100644 --- a/src/botPage/view/blockly/blocks/tools/time/todatetime.js +++ b/src/botPage/view/blockly/blocks/tools/time/todatetime.js @@ -5,7 +5,7 @@ import { translate } from '../../../../../../common/i18n'; Blockly.Blocks.todatetime = { init: function init() { this.appendDummyInput(); - this.appendValueInput('TIMESTAMP').appendField(translate('To Date/Time')); + this.appendValueInput('TIMESTAMP').appendField('To Date/Time'); this.setInputsInline(true); this.setOutput(true, 'String'); this.setColour('#dedede'); @@ -19,19 +19,18 @@ Blockly.Blocks.todatetime = { }; Blockly.JavaScript.todatetime = block => { - // const timestamp = Blockly.JavaScript.valueToCode(block, 'TIMESTAMP', Blockly.JavaScript.ORDER_ATOMIC); - // const code = `Bot.timestampToDateTimeString(${timestamp})`; - // return [code, Blockly.JavaScript.ORDER_ATOMIC]; - const timestamp = Blockly.JavaScript.valueToCode(block, 'TIMESTAMP', Blockly.JavaScript.ORDER_ATOMIC); // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('timestampToDateString', [ // eslint-disable-next-line no-underscore-dangle + `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(timestamp) { var dateTime; if(typeof timestamp == 'number') { dateTime = new Date(timestamp * 1000); + } else { + return '${translate('Invalid timestamp')}: ' + timestamp; } return [ dateTime.getFullYear().toString(), diff --git a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js index e757e8f150..1f97f8b366 100644 --- a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js +++ b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js @@ -31,8 +31,13 @@ Blockly.JavaScript.totimestamp = block => { const functionName = Blockly.JavaScript.provideFunction_('dateTimeStringToTimestamp', [ // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(dateTimeString) { - var date = new Date(dateTimeString); - return date ? date.getTime() / 1000 : 'Invalid date/time'; + var p = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\s(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))/; + if(p.test(dateTimeString)) { + var date = new Date(dateTimeString); + } else { + return '${translate('Invalid date/time')}: ' + dateTimeString; + } + return date ? date.getTime() / 1000 : '${translate('Invalid date/time')}: ' + dateTimeString; }`, ]); From 4fe495d923ebd5b58119bb9b1ba58614198ebe93 Mon Sep 17 00:00:00 2001 From: Anton Date: Tue, 11 Jun 2019 16:06:25 +0800 Subject: [PATCH 05/49] added tests --- src/botPage/bot/Interface/ToolsInterface.js | 27 ++++++++++++++++- .../__tests__/block-tests/tools-test/Time.js | 30 ++++++++++++++++++- .../blockly/blocks/tools/time/todatetime.js | 17 +---------- .../blockly/blocks/tools/time/totimestamp.js | 14 +-------- 4 files changed, 57 insertions(+), 31 deletions(-) diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index 90fb23f451..f4c1085ca8 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -1,13 +1,38 @@ import CandleInterface from './CandleInterface'; import MiscInterface from './MiscInterface'; import IndicatorsInterface from './IndicatorsInterface'; +import { translate } from '../../../common/i18n'; // prettier-ignore export default Interface => class extends IndicatorsInterface( MiscInterface(CandleInterface(Interface))) { getToolsInterface() { return { - getTime: () => parseInt(new Date().getTime() / 1000), + getTime : () => parseInt(new Date().getTime() / 1000), + toDateTime: (timestamp) => { + let dateTime; + if(typeof timestamp === 'number') { + dateTime = new Date(timestamp * 1000); + return `${[ + dateTime.getFullYear().toString(), + dateTime.getMonth()+1 < 10 ? `0${ dateTime.getMonth()+1}` : dateTime.getMonth()+1, + dateTime.getDate() < 10 ? `0${ dateTime.getDate()}` : dateTime.getDate(), + ].join('-') } ${[ + dateTime.getHours() < 10 ? `0${ dateTime.getHours()}` : dateTime.getHours(), + dateTime.getMinutes() < 10 ? `0${ dateTime.getMinutes()}` : dateTime.getMinutes(), + dateTime.getSeconds() < 10 ? `0${ dateTime.getSeconds()}` : dateTime.getSeconds(), + ].join(':')}`; + } + return `${translate('Invalid timestamp')}: ${timestamp}`; + }, + toTimestamp: (dateTimeString) => { + const p = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\s(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))/; + if(p.test(dateTimeString)) { + const date = new Date(dateTimeString); + return date ? date.getTime() / 1000 : `${translate('Invalid date/time')}: ${dateTimeString}`; + } + return `${translate('Invalid date/time')}: ${dateTimeString}`; + }, ...this.getCandleInterface(), ...this.getMiscInterface(), ...this.getIndicatorsInterface(), diff --git a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js index 344f02c738..66abb5596d 100644 --- a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js +++ b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { run } from '../../tools'; +import { run, runAndGetResult } from '../tools'; describe('Time in tools', () => { let result; @@ -27,3 +27,31 @@ describe('Time in tools', () => { expect(time2 - time1).most(3); }); }); + +describe('Convert to date/time string', () => { + const timestamp = '1560238593'; + let result; + beforeAll(done => { + runAndGetResult(undefined, `Bot.toDateTime(${timestamp})`).then(v => { + result = v; + done(); + }); + }); + it('converts timestamp to date/time string', () => { + expect(result).satisfy(dt => dt === '2019-06-11 15:36:33'); + }); +}); + +describe('Convert to timestamp', () => { + const dateTime = '2019-06-11 15:36:33'; + let result; + beforeAll(done => { + runAndGetResult(undefined, `Bot.toTimestamp(${dateTime})`).then(v => { + result = v; + done(); + }); + }); + it('converts date/time string to timestamp', () => { + expect(result).satisfy(ts => ts === '1560238593'); + }); +}); diff --git a/src/botPage/view/blockly/blocks/tools/time/todatetime.js b/src/botPage/view/blockly/blocks/tools/time/todatetime.js index 0746aef765..a0dae6d0b9 100644 --- a/src/botPage/view/blockly/blocks/tools/time/todatetime.js +++ b/src/botPage/view/blockly/blocks/tools/time/todatetime.js @@ -26,22 +26,7 @@ Blockly.JavaScript.todatetime = block => { // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(timestamp) { - var dateTime; - if(typeof timestamp == 'number') { - dateTime = new Date(timestamp * 1000); - } else { - return '${translate('Invalid timestamp')}: ' + timestamp; - } - return [ - dateTime.getFullYear().toString(), - dateTime.getMonth()+1 < 10 ? '0' + (dateTime.getMonth()+1) : dateTime.getMonth()+1, - dateTime.getDate() < 10 ? '0' + dateTime.getDate() : dateTime.getDate() - ].join('-') + ' ' + - [ - dateTime.getHours() < 10 ? '0' + dateTime.getHours() : dateTime.getHours(), - dateTime.getMinutes() < 10 ? '0' + dateTime.getMinutes() : dateTime.getMinutes(), - dateTime.getSeconds() < 10 ? '0' + dateTime.getSeconds() : dateTime.getSeconds() - ].join(':'); + return Bot.toDateTime(timestamp); }`, ]); diff --git a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js index 1f97f8b366..7ae8fc1342 100644 --- a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js +++ b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js @@ -20,24 +20,12 @@ Blockly.Blocks.totimestamp = { }; Blockly.JavaScript.totimestamp = block => { - // const dString = Blockly.JavaScript.valueToCode(block, 'DATETIME', Blockly.JavaScript.ORDER_ATOMIC); - // const code = `Bot.dateTimeStringToTimestamp('${dString}')`; - // return [code, Blockly.JavaScript.ORDER_NONE]; - - // YYYY-MM-DD HH:MM:SS format const dateString = Blockly.JavaScript.valueToCode(block, 'DATETIME', Blockly.JavaScript.ORDER_ATOMIC); - // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('dateTimeStringToTimestamp', [ // eslint-disable-next-line no-underscore-dangle `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(dateTimeString) { - var p = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\s(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))/; - if(p.test(dateTimeString)) { - var date = new Date(dateTimeString); - } else { - return '${translate('Invalid date/time')}: ' + dateTimeString; - } - return date ? date.getTime() / 1000 : '${translate('Invalid date/time')}: ' + dateTimeString; + return Bot.toTimestamp(dateTimeString); }`, ]); From a82e899e4f0153bd2fd994da6e532ed6ec0e9eb9 Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 12 Jun 2019 09:49:09 +0800 Subject: [PATCH 06/49] fixes following code review suggestions --- src/botPage/bot/Interface/ToolsInterface.js | 36 +++++++++++-------- .../__tests__/block-tests/tools-test/Time.js | 2 +- .../blockly/blocks/tools/time/todatetime.js | 7 +--- .../blockly/blocks/tools/time/totimestamp.js | 4 --- 4 files changed, 23 insertions(+), 26 deletions(-) diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index f4c1085ca8..b5a4ddcc1e 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -10,28 +10,34 @@ export default Interface => class extends IndicatorsInterface( return { getTime : () => parseInt(new Date().getTime() / 1000), toDateTime: (timestamp) => { - let dateTime; + const getTwoDigitValue = input => { + if (input < 10) { + return `0${input}`; + } + return `${input}`; + } if(typeof timestamp === 'number') { - dateTime = new Date(timestamp * 1000); - return `${[ - dateTime.getFullYear().toString(), - dateTime.getMonth()+1 < 10 ? `0${ dateTime.getMonth()+1}` : dateTime.getMonth()+1, - dateTime.getDate() < 10 ? `0${ dateTime.getDate()}` : dateTime.getDate(), - ].join('-') } ${[ - dateTime.getHours() < 10 ? `0${ dateTime.getHours()}` : dateTime.getHours(), - dateTime.getMinutes() < 10 ? `0${ dateTime.getMinutes()}` : dateTime.getMinutes(), - dateTime.getSeconds() < 10 ? `0${ dateTime.getSeconds()}` : dateTime.getSeconds(), - ].join(':')}`; + const dateTime = new Date(timestamp * 1000); + + const year = dateTime.getFullYear(); + const month = getTwoDigitValue(dateTime.getMonth() + 1); + const day = getTwoDigitValue(dateTime.getDate()); + const hours = getTwoDigitValue(dateTime.getHours()); + const minutes = getTwoDigitValue(dateTime.getMinutes()); + const seconds = getTwoDigitValue(dateTime.getSeconds()); + + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; } return `${translate('Invalid timestamp')}: ${timestamp}`; }, toTimestamp: (dateTimeString) => { - const p = /([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\s(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))/; - if(p.test(dateTimeString)) { + const p = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\s(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))$/; + const invalidDateTime = () => `${translate('Invalid date/time')}: ${dateTimeString}`; + if (p.test(dateTimeString)) { const date = new Date(dateTimeString); - return date ? date.getTime() / 1000 : `${translate('Invalid date/time')}: ${dateTimeString}`; + return date ? date.getTime() / 1000 : invalidDateTime(); } - return `${translate('Invalid date/time')}: ${dateTimeString}`; + return invalidDateTime(); }, ...this.getCandleInterface(), ...this.getMiscInterface(), diff --git a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js index 66abb5596d..224437f637 100644 --- a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js +++ b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { run, runAndGetResult } from '../tools'; +import { run, runAndGetResult } from '../../tools'; describe('Time in tools', () => { let result; diff --git a/src/botPage/view/blockly/blocks/tools/time/todatetime.js b/src/botPage/view/blockly/blocks/tools/time/todatetime.js index a0dae6d0b9..de821280ec 100644 --- a/src/botPage/view/blockly/blocks/tools/time/todatetime.js +++ b/src/botPage/view/blockly/blocks/tools/time/todatetime.js @@ -1,11 +1,9 @@ -// https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#wupwb4 import { translate } from '../../../../../../common/i18n'; -// import { disable } from '../../../utils'; Blockly.Blocks.todatetime = { init: function init() { this.appendDummyInput(); - this.appendValueInput('TIMESTAMP').appendField('To Date/Time'); + this.appendValueInput('TIMESTAMP').appendField(translate('To Date/Time')); this.setInputsInline(true); this.setOutput(true, 'String'); this.setColour('#dedede'); @@ -14,17 +12,14 @@ Blockly.Blocks.todatetime = { 'Converts a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' ) ); - // this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); }, }; Blockly.JavaScript.todatetime = block => { const timestamp = Blockly.JavaScript.valueToCode(block, 'TIMESTAMP', Blockly.JavaScript.ORDER_ATOMIC); - // eslint-disable-next-line no-underscore-dangle const functionName = Blockly.JavaScript.provideFunction_('timestampToDateString', [ // eslint-disable-next-line no-underscore-dangle - `function ${Blockly.JavaScript.FUNCTION_NAME_PLACEHOLDER_}(timestamp) { return Bot.toDateTime(timestamp); }`, diff --git a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js index 7ae8fc1342..849e698cf4 100644 --- a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js +++ b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js @@ -1,7 +1,4 @@ -// https://blockly-demo.appspot.com/static/demos/blockfactory/index.html#wupwb4 import { translate } from '../../../../../../common/i18n'; -// import { dateToTimeString } from 'binary-utils/lib'; -// import { disable } from '../../../utils'; Blockly.Blocks.totimestamp = { init: function init() { @@ -15,7 +12,6 @@ Blockly.Blocks.totimestamp = { 'Converts a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' ) ); - // this.setHelpUrl('https://github.com/binary-com/binary-bot/wiki'); }, }; From 9efa44a4d8f692448cde425394c288e1fed7476a Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 12 Jun 2019 13:14:49 +0800 Subject: [PATCH 07/49] fixed tests --- src/botPage/bot/__tests__/block-tests/tools-test/Time.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js index 224437f637..14dc4edbd1 100644 --- a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js +++ b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js @@ -1,5 +1,5 @@ import { expect } from 'chai'; -import { run, runAndGetResult } from '../../tools'; +import { run } from '../../tools'; describe('Time in tools', () => { let result; @@ -32,7 +32,7 @@ describe('Convert to date/time string', () => { const timestamp = '1560238593'; let result; beforeAll(done => { - runAndGetResult(undefined, `Bot.toDateTime(${timestamp})`).then(v => { + run(`(function() {return Bot.toDateTime(${timestamp});})()`).then(v => { result = v; done(); }); @@ -46,12 +46,12 @@ describe('Convert to timestamp', () => { const dateTime = '2019-06-11 15:36:33'; let result; beforeAll(done => { - runAndGetResult(undefined, `Bot.toTimestamp(${dateTime})`).then(v => { + run(`(function() {return Bot.toTimestamp('${dateTime}');})()`).then(v => { result = v; done(); }); }); it('converts date/time string to timestamp', () => { - expect(result).satisfy(ts => ts === '1560238593'); + expect(result).satisfy(ts => ts === 1560238593); }); }); From 60e76f7b8647138adcaeddca6581e24facd5e53b Mon Sep 17 00:00:00 2001 From: Anton Date: Wed, 12 Jun 2019 18:16:56 +0800 Subject: [PATCH 08/49] fix for timezone issue in tests --- .../__tests__/block-tests/tools-test/Time.js | 22 ++++--------------- 1 file changed, 4 insertions(+), 18 deletions(-) diff --git a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js index 14dc4edbd1..0c38235b8c 100644 --- a/src/botPage/bot/__tests__/block-tests/tools-test/Time.js +++ b/src/botPage/bot/__tests__/block-tests/tools-test/Time.js @@ -28,30 +28,16 @@ describe('Time in tools', () => { }); }); -describe('Convert to date/time string', () => { - const timestamp = '1560238593'; +describe('Convert timestamp to date/time and back', () => { + const timestamp = Math.ceil(new Date().getTime() / 1000); let result; beforeAll(done => { - run(`(function() {return Bot.toDateTime(${timestamp});})()`).then(v => { + run(`(function() {return Bot.toTimestamp(Bot.toDateTime(${timestamp}));})()`).then(v => { result = v; done(); }); }); it('converts timestamp to date/time string', () => { - expect(result).satisfy(dt => dt === '2019-06-11 15:36:33'); - }); -}); - -describe('Convert to timestamp', () => { - const dateTime = '2019-06-11 15:36:33'; - let result; - beforeAll(done => { - run(`(function() {return Bot.toTimestamp('${dateTime}');})()`).then(v => { - result = v; - done(); - }); - }); - it('converts date/time string to timestamp', () => { - expect(result).satisfy(ts => ts === 1560238593); + expect(result).satisfy(dt => dt === timestamp); }); }); From 2e3f587a332e762f173c96ba830f0172379ce466 Mon Sep 17 00:00:00 2001 From: McSam Date: Fri, 14 Jun 2019 11:15:42 +0800 Subject: [PATCH 09/49] fix trackjs-missing contract required parameters --- src/botPage/view/blockly/blocks/shared.js | 4 ++++ .../view/blockly/blocks/trade/tradeOptions.js | 23 ++++++++++++++----- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/botPage/view/blockly/blocks/shared.js b/src/botPage/view/blockly/blocks/shared.js index f0151c106f..476ad37177 100644 --- a/src/botPage/view/blockly/blocks/shared.js +++ b/src/botPage/view/blockly/blocks/shared.js @@ -409,3 +409,7 @@ export const getPredictionForContracts = (contracts, selectedContractType) => { } return predictionRange; }; + +export const disabledRunButton = flag => { + $('#runButton, #summaryRunButton').attr('disabled', flag); +}; diff --git a/src/botPage/view/blockly/blocks/trade/tradeOptions.js b/src/botPage/view/blockly/blocks/trade/tradeOptions.js index f3266c543f..3eaed46821 100644 --- a/src/botPage/view/blockly/blocks/trade/tradeOptions.js +++ b/src/botPage/view/blockly/blocks/trade/tradeOptions.js @@ -5,6 +5,7 @@ import { getDurationsForContracts, getBarriersForContracts, getPredictionForContracts, + disabledRunButton, } from '../shared'; import { insideTrade } from '../../relationChecker'; import { findTopParentBlock, hideInteractionsFromBlockly, getBlocksByType } from '../../utils'; @@ -102,8 +103,20 @@ export default () => { } }, pollForContracts(symbol) { + disabledRunButton(true); return new Promise(resolve => { const contractsForSymbol = haveContractsForSymbol(symbol); + + const resolvePollingFn = (pollingFn, resolveObj) => { + clearInterval(pollingFn); + resolvePollForContract(resolveObj); + }; + + const resolvePollForContract = resolveObj => { + disabledRunButton(false); + resolve(resolveObj); + }; + if (!contractsForSymbol) { // Register an event and use as a lock to avoid spamming API const event = `contractsLoaded.${symbol}`; @@ -111,24 +124,22 @@ export default () => { globalObserver.register(event, () => {}); getContractsAvailableForSymbol(symbol).then(contracts => { globalObserver.unregisterAll(event); // Release the lock - resolve(contracts); + resolvePollForContract(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); + resolvePollingFn(pollingFn, contracts.available); } }, 100); setTimeout(() => { - clearInterval(pollingFn); - resolve([]); + resolvePollingFn(pollingFn, []); }, 10000); } } else { - resolve(contractsForSymbol.available); + resolvePollForContract(contractsForSymbol.available); } }); }, From fac2c55ecd22e626e530bd50a73e9188b9db7ab4 Mon Sep 17 00:00:00 2001 From: Anton Date: Sat, 15 Jun 2019 13:21:00 +0800 Subject: [PATCH 10/49] GMT offset added, input validation refactored --- src/botPage/bot/Interface/ToolsInterface.js | 39 +++++++++++-------- .../blockly/blocks/tools/time/todatetime.js | 2 +- .../blockly/blocks/tools/time/totimestamp.js | 2 +- 3 files changed, 24 insertions(+), 19 deletions(-) diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index b5a4ddcc1e..43571f25bd 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -16,28 +16,33 @@ export default Interface => class extends IndicatorsInterface( } return `${input}`; } + const invalidTimestamp = () => `${translate('Invalid timestamp')}: ${timestamp}`; if(typeof timestamp === 'number') { const dateTime = new Date(timestamp * 1000); - - const year = dateTime.getFullYear(); - const month = getTwoDigitValue(dateTime.getMonth() + 1); - const day = getTwoDigitValue(dateTime.getDate()); - const hours = getTwoDigitValue(dateTime.getHours()); - const minutes = getTwoDigitValue(dateTime.getMinutes()); - const seconds = getTwoDigitValue(dateTime.getSeconds()); - - return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + if(dateTime.getTime()) { + const year = dateTime.getFullYear(); + const month = getTwoDigitValue(dateTime.getMonth() + 1); + const day = getTwoDigitValue(dateTime.getDate()); + const hours = getTwoDigitValue(dateTime.getHours()); + const minutes = getTwoDigitValue(dateTime.getMinutes()); + const seconds = getTwoDigitValue(dateTime.getSeconds()); + const formatGTMoffset = () => { + const GMToffsetRaw = dateTime.getTimezoneOffset(); + const sign = GMToffsetRaw > 0 ? '-' : '+'; + const GMToffset = Math.abs(GMToffsetRaw); + const h = Math.floor(GMToffset / 60); + const m = GMToffset - h * 60; + return `GMT${sign}${getTwoDigitValue(h)}${getTwoDigitValue(m)}`; + } + return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${formatGTMoffset()}`; + } + return invalidTimestamp(); } - return `${translate('Invalid timestamp')}: ${timestamp}`; + return invalidTimestamp(); }, toTimestamp: (dateTimeString) => { - const p = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])\s(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9]))$/; - const invalidDateTime = () => `${translate('Invalid date/time')}: ${dateTimeString}`; - if (p.test(dateTimeString)) { - const date = new Date(dateTimeString); - return date ? date.getTime() / 1000 : invalidDateTime(); - } - return invalidDateTime(); + const date = new Date(dateTimeString.substr(0,19)).getTime(); + return date ? Math.floor(date / 1000) : `${translate('Invalid date/time')}: ${dateTimeString}`; }, ...this.getCandleInterface(), ...this.getMiscInterface(), diff --git a/src/botPage/view/blockly/blocks/tools/time/todatetime.js b/src/botPage/view/blockly/blocks/tools/time/todatetime.js index de821280ec..df1e981408 100644 --- a/src/botPage/view/blockly/blocks/tools/time/todatetime.js +++ b/src/botPage/view/blockly/blocks/tools/time/todatetime.js @@ -9,7 +9,7 @@ Blockly.Blocks.todatetime = { this.setColour('#dedede'); this.setTooltip( translate( - 'Converts a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' + 'Converts a number of seconds since Epoch into a string representing date and time. Example: 1546347825 will be converted to 2019-01-01 21:03:45.' ) ); }, diff --git a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js index 849e698cf4..8fdc7330c6 100644 --- a/src/botPage/view/blockly/blocks/tools/time/totimestamp.js +++ b/src/botPage/view/blockly/blocks/tools/time/totimestamp.js @@ -9,7 +9,7 @@ Blockly.Blocks.totimestamp = { this.setColour('#dedede'); this.setTooltip( translate( - 'Converts a string representing a date/time string into seconds since Epoch. Input format: yyyy-mm-dd hh:mm:ss' + 'Converts a string representing a date/time string into seconds since Epoch. Example: 2019-01-01 21:03:45 GMT+0800 will be converted to 1546347825. Time and time zone offset are optional.' ) ); }, From e2b3dc0f47086595f50f58848de539413de81655 Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 16 Jun 2019 17:37:02 +0800 Subject: [PATCH 11/49] toolbox width adjusted --- static/css/_blockly-toolbox.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/_blockly-toolbox.scss b/static/css/_blockly-toolbox.scss index a3f8870445..57389243ea 100644 --- a/static/css/_blockly-toolbox.scss +++ b/static/css/_blockly-toolbox.scss @@ -59,7 +59,7 @@ border-width: thin; color: $brand-dark-gray; border-right: 0.063em solid; - width: 11em; + width: 15em; } .blocklyIconShape { From 8caf1eb6faa626a78d638a2a6349bfa9d6331d2e Mon Sep 17 00:00:00 2001 From: McSam Date: Mon, 17 Jun 2019 10:12:42 +0800 Subject: [PATCH 12/49] resolved run button enabled after market type changed when the contract status is pending --- src/botPage/view/View.js | 18 ++++++++++++++---- src/botPage/view/blockly/blocks/shared.js | 4 ++-- .../view/blockly/blocks/trade/tradeOptions.js | 15 +++++++-------- src/botPage/view/blockly/index.js | 7 +------ static/css/_toolbox.scss | 2 +- 5 files changed, 25 insertions(+), 21 deletions(-) diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index 9dd37c1b1e..293844e0d7 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -511,9 +511,11 @@ export default class View { }); const startBot = limitations => { - $('#stopButton, #summaryStopButton').show(); - $('#runButton, #summaryRunButton').hide(); - $('#runButton, #summaryRunButton').prop('disabled', true); + const $runButton = $('#runButton, #summaryRunButton'); + const $stopButton = $('#stopButton, #summaryStopButton'); + $stopButton.show(); + $runButton.hide(); + $runButton.prop('disabled', true); globalObserver.emit('summary.disable_clear'); showSummary(); this.blockly.run(limitations); @@ -641,7 +643,15 @@ export default class View { }); globalObserver.register('bot.stop', () => { - $('#runButton, #summaryRunButton').prop('disabled', false); + const $runButtons = $('#runButton, #summaryRunButton'); + const $stopButtons = $('#stopButton, #summaryStopButton'); + if ($runButtons.is(':visible') || $stopButtons.is(':visible')) { + $runButtons.show(); + $stopButtons.hide(); + + $stopButtons.prop('disabled', false); + $runButtons.prop('disabled', false); + } }); globalObserver.register('bot.info', info => { diff --git a/src/botPage/view/blockly/blocks/shared.js b/src/botPage/view/blockly/blocks/shared.js index 476ad37177..10d42ed44a 100644 --- a/src/botPage/view/blockly/blocks/shared.js +++ b/src/botPage/view/blockly/blocks/shared.js @@ -410,6 +410,6 @@ export const getPredictionForContracts = (contracts, selectedContractType) => { return predictionRange; }; -export const disabledRunButton = flag => { - $('#runButton, #summaryRunButton').attr('disabled', flag); +export const disableRunButton = isDisabled => { + $('#runButton, #summaryRunButton').attr('disabled', isDisabled); }; diff --git a/src/botPage/view/blockly/blocks/trade/tradeOptions.js b/src/botPage/view/blockly/blocks/trade/tradeOptions.js index 3eaed46821..4b0976ef7d 100644 --- a/src/botPage/view/blockly/blocks/trade/tradeOptions.js +++ b/src/botPage/view/blockly/blocks/trade/tradeOptions.js @@ -5,7 +5,7 @@ import { getDurationsForContracts, getBarriersForContracts, getPredictionForContracts, - disabledRunButton, + disableRunButton, } from '../shared'; import { insideTrade } from '../../relationChecker'; import { findTopParentBlock, hideInteractionsFromBlockly, getBlocksByType } from '../../utils'; @@ -103,17 +103,16 @@ export default () => { } }, pollForContracts(symbol) { - disabledRunButton(true); + disableRunButton(true); return new Promise(resolve => { const contractsForSymbol = haveContractsForSymbol(symbol); - const resolvePollingFn = (pollingFn, resolveObj) => { clearInterval(pollingFn); - resolvePollForContract(resolveObj); + resolveContracts(resolveObj); }; - const resolvePollForContract = resolveObj => { - disabledRunButton(false); + const resolveContracts = resolveObj => { + disableRunButton(false); resolve(resolveObj); }; @@ -124,7 +123,7 @@ export default () => { globalObserver.register(event, () => {}); getContractsAvailableForSymbol(symbol).then(contracts => { globalObserver.unregisterAll(event); // Release the lock - resolvePollForContract(contracts); + resolveContracts(contracts); }); } else { // Request in progress, start polling localStorage until contracts are available. @@ -139,7 +138,7 @@ export default () => { }, 10000); } } else { - resolvePollForContract(contractsForSymbol.available); + resolveContracts(contractsForSymbol.available); } }); }, diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index 0636724381..ce92b40a5b 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -409,12 +409,7 @@ while(true) { } stop(stopBeforeStart) { if (!stopBeforeStart) { - const $runButtons = $('#runButton, #summaryRunButton'); - const $stopButtons = $('#stopButton, #summaryStopButton'); - if ($runButtons.is(':visible') || $stopButtons.is(':visible')) { - $runButtons.show(); - $stopButtons.hide(); - } + $('#stopButton, #summaryStopButton').prop('disabled', true); } if (this.interpreter) { this.interpreter.stop(); diff --git a/static/css/_toolbox.scss b/static/css/_toolbox.scss index 79daa64c74..866d3a7146 100644 --- a/static/css/_toolbox.scss +++ b/static/css/_toolbox.scss @@ -27,7 +27,7 @@ z-index: 0; overflow: auto; - #runButton[disabled], #runButton[disabled]:hover { + #runButton[disabled], #runButton[disabled]:hover, #stopButton[disabled], #stopButton[disabled] { @include toolbox-runButton-disabled; } From 27f11c14215d9cf50af69bf2deae57e0b4c44358 Mon Sep 17 00:00:00 2001 From: McSam Date: Mon, 17 Jun 2019 16:49:37 +0800 Subject: [PATCH 13/49] + Download Context Menu for 'Variables' & 'Loops' blocks --- src/botPage/view/blockly/customBlockly.js | 261 ++++++++++++++++++++++ 1 file changed, 261 insertions(+) diff --git a/src/botPage/view/blockly/customBlockly.js b/src/botPage/view/blockly/customBlockly.js index db1fa80e52..625cbc8157 100644 --- a/src/botPage/view/blockly/customBlockly.js +++ b/src/botPage/view/blockly/customBlockly.js @@ -1,6 +1,7 @@ import GTM from '../../../common/gtm'; import { translate, translateLangToLang } from '../../../common/i18n'; import { getLanguage } from '../../../common/lang'; +import { save } from './utils'; /* eslint-disable */ Blockly.WorkspaceAudio.prototype.preload = function() {}; @@ -358,3 +359,263 @@ Blockly.Toolbox.TreeNode.prototype.onClick_ = function(_e) { } this.updateRow(); }; + +// https://groups.google.com/forum/#!msg/blockly/eS1V49pI9c8/VEh5UuUcBAAJ +// Custom Variable Block with Download Context Menu +Blockly.defineBlocksWithJsonArray([ + // BEGIN JSON EXTRACT + // Block for variable getter. + { + type: 'variables_get', + message0: '%1', + args0: [ + { + type: 'field_variable', + name: 'VAR', + variable: '%{BKY_VARIABLES_DEFAULT_NAME}', + }, + ], + colour: '#DEDEDE', + output: null, + helpUrl: '%{BKY_VARIABLES_GET_HELPURL}', + tooltip: '%{BKY_VARIABLES_GET_TOOLTIP}', + extensions: ['customContextMenu_variableSetterGetter'], + }, + // Block for variable setter. + { + type: 'variables_set', + message0: '%{BKY_VARIABLES_SET}', + args0: [ + { + type: 'field_variable', + name: 'VAR', + variable: '%{BKY_VARIABLES_DEFAULT_NAME}', + }, + { + type: 'input_value', + name: 'VALUE', + }, + ], + colour: '#DEDEDE', + previousStatement: null, + nextStatement: null, + tooltip: '%{BKY_VARIABLES_SET_TOOLTIP}', + helpUrl: '%{BKY_VARIABLES_SET_HELPURL}', + extensions: ['customContextMenu_variableSetterGetter'], + }, +]); // END JSON EXTRACT (Do not delete this comment.) + +/** + * Mixin to add context menu items to create getter/setter blocks for this + * setter/getter. + * Used by blocks 'variables_set' and 'variables_get'. + * @mixin + * @augments Blockly.Block + * @package + * @readonly + */ +Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { + /** + * Add menu option to create getter/setter block for this setter/getter. + * @param {!Array} options List of menu options to add to. + * @this Blockly.Block + */ + customContextMenu: function(options) { + if (!this.isInFlyout) { + // Getter blocks have the option to create a setter block, and vice versa. + if (this.type == 'variables_get') { + var opposite_type = 'variables_set'; + var contextMenuMsg = Blockly.Msg['VARIABLES_GET_CREATE_SET']; + } else { + var opposite_type = 'variables_get'; + var contextMenuMsg = Blockly.Msg['VARIABLES_SET_CREATE_GET']; + } + + var option = { enabled: this.workspace.remainingCapacity() > 0 }; + var name = this.getField('VAR').getText(); + option.text = contextMenuMsg.replace('%1', name); + var xmlField = document.createElement('field'); + xmlField.setAttribute('name', 'VAR'); + xmlField.appendChild(document.createTextNode(name)); + var xmlBlock = document.createElement('block'); + xmlBlock.setAttribute('type', opposite_type); + xmlBlock.appendChild(xmlField); + option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + options.push(option); + + const downloadOption = { + text: translate('Download'), + enabled: true, + callback: () => { + const xml = Blockly.Xml.textToDom( + '' + ); + xml.appendChild(Blockly.Xml.blockToDom(this)); + save('binary-bot-block', true, xml); + }, + }; + options.push(downloadOption); + // Getter blocks have the option to rename or delete that variable. + } else { + if (this.type == 'variables_get' || this.type == 'variables_get_reporter') { + var renameOption = { + text: Blockly.Msg.RENAME_VARIABLE, + enabled: true, + callback: Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this), + }; + var name = this.getField('VAR').getText(); + var deleteOption = { + text: Blockly.Msg.DELETE_VARIABLE.replace('%1', name), + enabled: true, + callback: Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this), + }; + options.unshift(renameOption); + options.unshift(deleteOption); + } + } + }, +}; + +Blockly.Extensions.registerMixin( + 'customContextMenu_variableSetterGetter', + Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN +); + +// Custom Loop block with download Context Menu +Blockly.defineBlocksWithJsonArray([ + // Block for 'for' loop. + { + type: 'controls_for', + message0: '%{BKY_CONTROLS_FOR_TITLE}', + args0: [ + { + type: 'field_variable', + name: 'VAR', + variable: null, + }, + { + type: 'input_value', + name: 'FROM', + check: 'Number', + align: 'RIGHT', + }, + { + type: 'input_value', + name: 'TO', + check: 'Number', + align: 'RIGHT', + }, + { + type: 'input_value', + name: 'BY', + check: 'Number', + align: 'RIGHT', + }, + ], + message1: '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', + args1: [ + { + type: 'input_statement', + name: 'DO', + }, + ], + colour: '#DEDEDE', + inputsInline: true, + previousStatement: null, + nextStatement: null, + style: 'loop_blocks', + helpUrl: '%{BKY_CONTROLS_FOR_HELPURL}', + extensions: ['customContextMenu_newGetVariableBlock', 'controls_for_customTooltip'], + }, + // Block for 'for each' loop. + { + type: 'controls_forEach', + message0: '%{BKY_CONTROLS_FOREACH_TITLE}', + args0: [ + { + type: 'field_variable', + name: 'VAR', + variable: null, + }, + { + type: 'input_value', + name: 'LIST', + check: 'Array', + }, + ], + message1: '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', + args1: [ + { + type: 'input_statement', + name: 'DO', + }, + ], + colour: '#DEDEDE', + previousStatement: null, + nextStatement: null, + style: 'loop_blocks', + helpUrl: '%{BKY_CONTROLS_FOREACH_HELPURL}', + extensions: ['customContextMenu_newGetVariableBlock', 'controls_forEach_customTooltip'], + }, +]); + +/** + * Mixin to add a context menu item to create a 'variables_get' block. + * Used by blocks 'controls_for' and 'controls_forEach'. + * @mixin + * @augments Blockly.Block + * @package + * @readonly + */ +Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = { + /** + * Add context menu option to create getter block for the loop's variable. + * (customContextMenu support limited to web BlockSvg.) + * @param {!Array} options List of menu options to add to. + * @this Blockly.Block + */ + customContextMenu: function(options) { + if (this.isInFlyout) { + return; + } + var variable = this.getField('VAR').getVariable(); + var varName = variable.name; + if (!this.isCollapsed() && varName != null) { + var option = { enabled: true }; + option.text = Blockly.Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName); + var xmlField = Blockly.Variables.generateVariableFieldDom(variable); + var xmlBlock = document.createElement('block'); + xmlBlock.setAttribute('type', 'variables_get'); + xmlBlock.appendChild(xmlField); + option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); + options.push(option); + } + const downloadOption = { + text: translate('Download'), + enabled: true, + callback: () => { + const xml = Blockly.Xml.textToDom( + '' + ); + xml.appendChild(Blockly.Xml.blockToDom(this)); + save('binary-bot-block', true, xml); + }, + }; + options.push(downloadOption); + }, +}; + +Blockly.Extensions.registerMixin( + 'customContextMenu_newGetVariableBlock', + Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN +); + +Blockly.Extensions.register( + 'controls_for_customTooltip', + Blockly.Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR') +); + +Blockly.Extensions.register( + 'controls_forEach_customTooltip', + Blockly.Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR') +); From dad1a72084b0a77002c73802f0e2ed31cda15569 Mon Sep 17 00:00:00 2001 From: McSam Date: Tue, 18 Jun 2019 14:17:39 +0800 Subject: [PATCH 14/49] Change contract detail data type from string to number --- src/botPage/bot/__tests__/block-tests/After.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/bot/__tests__/block-tests/After.js b/src/botPage/bot/__tests__/block-tests/After.js index 007e3c4ab0..b6c35e8c60 100644 --- a/src/botPage/bot/__tests__/block-tests/After.js +++ b/src/botPage/bot/__tests__/block-tests/After.js @@ -21,7 +21,7 @@ describe('After Purchase Blocks', () => { it('After purchase api', () => { expectResultTypes(result, [ 'boolean', // is result win - 'string', // statement + 'number', // statement ]); }); }); From 7296aac4151ff6821d2765139a8a4766c696ef96 Mon Sep 17 00:00:00 2001 From: McSam Date: Tue, 18 Jun 2019 14:48:17 +0800 Subject: [PATCH 15/49] npm test issue --- src/botPage/bot/__tests__/block-tests/After.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/bot/__tests__/block-tests/After.js b/src/botPage/bot/__tests__/block-tests/After.js index 007e3c4ab0..b6c35e8c60 100644 --- a/src/botPage/bot/__tests__/block-tests/After.js +++ b/src/botPage/bot/__tests__/block-tests/After.js @@ -21,7 +21,7 @@ describe('After Purchase Blocks', () => { it('After purchase api', () => { expectResultTypes(result, [ 'boolean', // is result win - 'string', // statement + 'number', // statement ]); }); }); From 10a0aac01fd0b4fa0c0cd87ac6ad9ca2d4cb893e Mon Sep 17 00:00:00 2001 From: McSam Date: Tue, 18 Jun 2019 15:06:47 +0800 Subject: [PATCH 16/49] restart travis From 84c460cb777191c3596fd3c3a04fbf2c83bafd43 Mon Sep 17 00:00:00 2001 From: McSam Date: Thu, 20 Jun 2019 11:51:18 +0800 Subject: [PATCH 17/49] resolve changes --- src/botPage/view/blockly/blocks/trade/tradeOptions.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/botPage/view/blockly/blocks/trade/tradeOptions.js b/src/botPage/view/blockly/blocks/trade/tradeOptions.js index 4b0976ef7d..91afba0ab6 100644 --- a/src/botPage/view/blockly/blocks/trade/tradeOptions.js +++ b/src/botPage/view/blockly/blocks/trade/tradeOptions.js @@ -130,11 +130,13 @@ export default () => { const pollingFn = setInterval(() => { const contracts = haveContractsForSymbol(symbol); if (contracts) { - resolvePollingFn(pollingFn, contracts.available); + clearInterval(pollingFn); + resolveContracts(contracts.available); } }, 100); setTimeout(() => { - resolvePollingFn(pollingFn, []); + clearInterval(pollingFn); + resolveContracts([]); }, 10000); } } else { From 9f31d6980438b4c692c8647372d5f476b1ff7b28 Mon Sep 17 00:00:00 2001 From: McSam Date: Thu, 20 Jun 2019 12:04:07 +0800 Subject: [PATCH 18/49] delete non-use function --- src/botPage/view/blockly/blocks/trade/tradeOptions.js | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/botPage/view/blockly/blocks/trade/tradeOptions.js b/src/botPage/view/blockly/blocks/trade/tradeOptions.js index 91afba0ab6..83581524c3 100644 --- a/src/botPage/view/blockly/blocks/trade/tradeOptions.js +++ b/src/botPage/view/blockly/blocks/trade/tradeOptions.js @@ -106,10 +106,6 @@ export default () => { disableRunButton(true); return new Promise(resolve => { const contractsForSymbol = haveContractsForSymbol(symbol); - const resolvePollingFn = (pollingFn, resolveObj) => { - clearInterval(pollingFn); - resolveContracts(resolveObj); - }; const resolveContracts = resolveObj => { disableRunButton(false); From 7616cf88c12d296982e4f2db899fe35e6d9eba7b Mon Sep 17 00:00:00 2001 From: McSam Date: Thu, 20 Jun 2019 12:20:51 +0800 Subject: [PATCH 19/49] npm test --- src/botPage/bot/__tests__/block-tests/After.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/bot/__tests__/block-tests/After.js b/src/botPage/bot/__tests__/block-tests/After.js index 007e3c4ab0..b6c35e8c60 100644 --- a/src/botPage/bot/__tests__/block-tests/After.js +++ b/src/botPage/bot/__tests__/block-tests/After.js @@ -21,7 +21,7 @@ describe('After Purchase Blocks', () => { it('After purchase api', () => { expectResultTypes(result, [ 'boolean', // is result win - 'string', // statement + 'number', // statement ]); }); }); From c3afc008893f055f131265b4d87ef2e5b803bb4f Mon Sep 17 00:00:00 2001 From: sam-binary <51310435+sam-binary@users.noreply.github.com> Date: Thu, 20 Jun 2019 13:49:36 +0800 Subject: [PATCH 20/49] Update src/botPage/view/View.js Co-Authored-By: Aaron --- src/botPage/view/View.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index 293844e0d7..24c967b9de 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -511,7 +511,7 @@ export default class View { }); const startBot = limitations => { - const $runButton = $('#runButton, #summaryRunButton'); + const $runButtons = $('#runButton, #summaryRunButton'); const $stopButton = $('#stopButton, #summaryStopButton'); $stopButton.show(); $runButton.hide(); From 81da4517378e88b9b875374ce6f6c4505f275228 Mon Sep 17 00:00:00 2001 From: sam-binary <51310435+sam-binary@users.noreply.github.com> Date: Thu, 20 Jun 2019 13:49:49 +0800 Subject: [PATCH 21/49] Update src/botPage/view/View.js Co-Authored-By: Aaron --- src/botPage/view/View.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index 24c967b9de..6ae08fab61 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -512,7 +512,7 @@ export default class View { const startBot = limitations => { const $runButtons = $('#runButton, #summaryRunButton'); - const $stopButton = $('#stopButton, #summaryStopButton'); + const $stopButtons = $('#stopButton, #summaryStopButton'); $stopButton.show(); $runButton.hide(); $runButton.prop('disabled', true); From 0cc5c77af7ed442665314eee051b621dea4aebbf Mon Sep 17 00:00:00 2001 From: McSam Date: Thu, 20 Jun 2019 14:30:26 +0800 Subject: [PATCH 22/49] var error --- src/botPage/view/View.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index 6ae08fab61..690a9552ae 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -513,9 +513,9 @@ export default class View { const startBot = limitations => { const $runButtons = $('#runButton, #summaryRunButton'); const $stopButtons = $('#stopButton, #summaryStopButton'); - $stopButton.show(); - $runButton.hide(); - $runButton.prop('disabled', true); + $stopButtons.show(); + $runButtons.hide(); + $runButtons.prop('disabled', true); globalObserver.emit('summary.disable_clear'); showSummary(); this.blockly.run(limitations); From 0331c76f8c864db55e988ff0c4648488bef1cec1 Mon Sep 17 00:00:00 2001 From: McSam Date: Thu, 20 Jun 2019 14:39:17 +0800 Subject: [PATCH 23/49] xml error --- src/botPage/view/blockly/index.js | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index 0636724381..9c3981bd33 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -317,6 +317,18 @@ export default class _Blockly { load(blockStr = '', dropEvent = {}) { let xml; + try { + const xmlDoc = new DOMParser().parseFromString(blockStr, 'application/xml'); + + if (xmlDoc.getElementsByTagName('parsererror').length > 0) { + globalObserver.emit('ui.log.warn', `${translate('Unrecognized file format')}`); + return; + } + } catch (err) { + globalObserver.emit('ui.log.warn', `${translate('Unrecognized file format')}`); + return; + } + try { xml = Blockly.Xml.textToDom(blockStr); } catch (e) { From 0688fcc4e13f8604ab707a95fcbae9785f1824e2 Mon Sep 17 00:00:00 2001 From: McSam Date: Thu, 20 Jun 2019 15:24:04 +0800 Subject: [PATCH 24/49] resolve changes --- src/botPage/view/blockly/customBlockly.js | 274 ++-------------------- 1 file changed, 20 insertions(+), 254 deletions(-) diff --git a/src/botPage/view/blockly/customBlockly.js b/src/botPage/view/blockly/customBlockly.js index 1858302bcd..b4f75f1cec 100644 --- a/src/botPage/view/blockly/customBlockly.js +++ b/src/botPage/view/blockly/customBlockly.js @@ -379,261 +379,27 @@ Blockly.WorkspaceAudio.prototype.preload = function() { }; // https://groups.google.com/forum/#!msg/blockly/eS1V49pI9c8/VEh5UuUcBAAJ -// Custom Variable Block with Download Context Menu -Blockly.defineBlocksWithJsonArray([ - // BEGIN JSON EXTRACT - // Block for variable getter. - { - type: 'variables_get', - message0: '%1', - args0: [ - { - type: 'field_variable', - name: 'VAR', - variable: '%{BKY_VARIABLES_DEFAULT_NAME}', - }, - ], - colour: '#DEDEDE', - output: null, - helpUrl: '%{BKY_VARIABLES_GET_HELPURL}', - tooltip: '%{BKY_VARIABLES_GET_TOOLTIP}', - extensions: ['customContextMenu_variableSetterGetter'], - }, - // Block for variable setter. - { - type: 'variables_set', - message0: '%{BKY_VARIABLES_SET}', - args0: [ - { - type: 'field_variable', - name: 'VAR', - variable: '%{BKY_VARIABLES_DEFAULT_NAME}', - }, - { - type: 'input_value', - name: 'VALUE', - }, - ], - colour: '#DEDEDE', - previousStatement: null, - nextStatement: null, - tooltip: '%{BKY_VARIABLES_SET_TOOLTIP}', - helpUrl: '%{BKY_VARIABLES_SET_HELPURL}', - extensions: ['customContextMenu_variableSetterGetter'], - }, -]); // END JSON EXTRACT (Do not delete this comment.) - -/** - * Mixin to add context menu items to create getter/setter blocks for this - * setter/getter. - * Used by blocks 'variables_set' and 'variables_get'. - * @mixin - * @augments Blockly.Block - * @package - * @readonly - */ -Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN = { - /** - * Add menu option to create getter/setter block for this setter/getter. - * @param {!Array} options List of menu options to add to. - * @this Blockly.Block - */ - customContextMenu: function(options) { - if (!this.isInFlyout) { - // Getter blocks have the option to create a setter block, and vice versa. - if (this.type == 'variables_get') { - var opposite_type = 'variables_set'; - var contextMenuMsg = Blockly.Msg['VARIABLES_GET_CREATE_SET']; - } else { - var opposite_type = 'variables_get'; - var contextMenuMsg = Blockly.Msg['VARIABLES_SET_CREATE_GET']; - } - - var option = { enabled: this.workspace.remainingCapacity() > 0 }; - var name = this.getField('VAR').getText(); - option.text = contextMenuMsg.replace('%1', name); - var xmlField = document.createElement('field'); - xmlField.setAttribute('name', 'VAR'); - xmlField.appendChild(document.createTextNode(name)); - var xmlBlock = document.createElement('block'); - xmlBlock.setAttribute('type', opposite_type); - xmlBlock.appendChild(xmlField); - option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); - options.push(option); - - const downloadOption = { - text: translate('Download'), - enabled: true, - callback: () => { - const xml = Blockly.Xml.textToDom( - '' - ); - xml.appendChild(Blockly.Xml.blockToDom(this)); - save('binary-bot-block', true, xml); - }, - }; - options.push(downloadOption); - // Getter blocks have the option to rename or delete that variable. - } else { - if (this.type == 'variables_get' || this.type == 'variables_get_reporter') { - var renameOption = { - text: Blockly.Msg.RENAME_VARIABLE, - enabled: true, - callback: Blockly.Constants.Variables.RENAME_OPTION_CALLBACK_FACTORY(this), - }; - var name = this.getField('VAR').getText(); - var deleteOption = { - text: Blockly.Msg.DELETE_VARIABLE.replace('%1', name), - enabled: true, - callback: Blockly.Constants.Variables.DELETE_OPTION_CALLBACK_FACTORY(this), - }; - options.unshift(renameOption); - options.unshift(deleteOption); - } - } - }, +const addDownloadOption = (callback, options, block) => { + options.push({ + text: translate('Download'), + enabled: true, + callback: () => { + const xml = Blockly.Xml.textToDom(''); + xml.appendChild(Blockly.Xml.blockToDom(block)); + save('binary-bot-block', true, xml); + }, + }); + callback(options); }; -Blockly.Extensions.registerMixin( - 'customContextMenu_variableSetterGetter', - Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN -); - -// Custom Loop block with download Context Menu -Blockly.defineBlocksWithJsonArray([ - // Block for 'for' loop. - { - type: 'controls_for', - message0: '%{BKY_CONTROLS_FOR_TITLE}', - args0: [ - { - type: 'field_variable', - name: 'VAR', - variable: null, - }, - { - type: 'input_value', - name: 'FROM', - check: 'Number', - align: 'RIGHT', - }, - { - type: 'input_value', - name: 'TO', - check: 'Number', - align: 'RIGHT', - }, - { - type: 'input_value', - name: 'BY', - check: 'Number', - align: 'RIGHT', - }, - ], - message1: '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', - args1: [ - { - type: 'input_statement', - name: 'DO', - }, - ], - colour: '#DEDEDE', - inputsInline: true, - previousStatement: null, - nextStatement: null, - style: 'loop_blocks', - helpUrl: '%{BKY_CONTROLS_FOR_HELPURL}', - extensions: ['customContextMenu_newGetVariableBlock', 'controls_for_customTooltip'], - }, - // Block for 'for each' loop. - { - type: 'controls_forEach', - message0: '%{BKY_CONTROLS_FOREACH_TITLE}', - args0: [ - { - type: 'field_variable', - name: 'VAR', - variable: null, - }, - { - type: 'input_value', - name: 'LIST', - check: 'Array', - }, - ], - message1: '%{BKY_CONTROLS_REPEAT_INPUT_DO} %1', - args1: [ - { - type: 'input_statement', - name: 'DO', - }, - ], - colour: '#DEDEDE', - previousStatement: null, - nextStatement: null, - style: 'loop_blocks', - helpUrl: '%{BKY_CONTROLS_FOREACH_HELPURL}', - extensions: ['customContextMenu_newGetVariableBlock', 'controls_forEach_customTooltip'], - }, -]); - -/** - * Mixin to add a context menu item to create a 'variables_get' block. - * Used by blocks 'controls_for' and 'controls_forEach'. - * @mixin - * @augments Blockly.Block - * @package - * @readonly - */ -Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN = { - /** - * Add context menu option to create getter block for the loop's variable. - * (customContextMenu support limited to web BlockSvg.) - * @param {!Array} options List of menu options to add to. - * @this Blockly.Block - */ - customContextMenu: function(options) { - if (this.isInFlyout) { - return; - } - var variable = this.getField('VAR').getVariable(); - var varName = variable.name; - if (!this.isCollapsed() && varName != null) { - var option = { enabled: true }; - option.text = Blockly.Msg['VARIABLES_SET_CREATE_GET'].replace('%1', varName); - var xmlField = Blockly.Variables.generateVariableFieldDom(variable); - var xmlBlock = document.createElement('block'); - xmlBlock.setAttribute('type', 'variables_get'); - xmlBlock.appendChild(xmlField); - option.callback = Blockly.ContextMenu.callbackFactory(this, xmlBlock); - options.push(option); - } - const downloadOption = { - text: translate('Download'), - enabled: true, - callback: () => { - const xml = Blockly.Xml.textToDom( - '' - ); - xml.appendChild(Blockly.Xml.blockToDom(this)); - save('binary-bot-block', true, xml); - }, - }; - options.push(downloadOption); - }, +const originalCustomContextVarFn = + Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN.customContextMenu; +Blockly.Constants.Variables.CUSTOM_CONTEXT_MENU_VARIABLE_GETTER_SETTER_MIXIN.customContextMenu = function(options) { + addDownloadOption(originalCustomContextVarFn.bind(this), options, this); }; -Blockly.Extensions.registerMixin( - 'customContextMenu_newGetVariableBlock', - Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN -); - -Blockly.Extensions.register( - 'controls_for_customTooltip', - Blockly.Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOR_TOOLTIP}', 'VAR') -); - -Blockly.Extensions.register( - 'controls_forEach_customTooltip', - Blockly.Extensions.buildTooltipWithFieldText('%{BKY_CONTROLS_FOREACH_TOOLTIP}', 'VAR') -); +const originalCustomContextLoopFn = + Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN.customContextMenu; +Blockly.Constants.Loops.CUSTOM_CONTEXT_MENU_CREATE_VARIABLES_GET_MIXIN.customContextMenu = function(options) { + addDownloadOption(originalCustomContextLoopFn.bind(this), options, this); +}; From 3ace537fdf833dfd952342d45c36c842b89252d7 Mon Sep 17 00:00:00 2001 From: anton-binary <43776314+anton-binary@users.noreply.github.com> Date: Fri, 21 Jun 2019 16:04:38 +0800 Subject: [PATCH 25/49] Update static/css/_blockly-toolbox.scss Co-Authored-By: Aaron --- static/css/_blockly-toolbox.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/_blockly-toolbox.scss b/static/css/_blockly-toolbox.scss index 57389243ea..3ec6e5818c 100644 --- a/static/css/_blockly-toolbox.scss +++ b/static/css/_blockly-toolbox.scss @@ -59,7 +59,7 @@ border-width: thin; color: $brand-dark-gray; border-right: 0.063em solid; - width: 15em; + min-width: 11em; } .blocklyIconShape { From d670ca51a656589d7bc764a14bcc3212275b9486 Mon Sep 17 00:00:00 2001 From: Anton Date: Sun, 23 Jun 2019 14:56:16 +0800 Subject: [PATCH 26/49] fixed safari bugs and date/time validation --- src/botPage/bot/Interface/ToolsInterface.js | 22 +++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index 43571f25bd..f051a15c97 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -17,9 +17,9 @@ export default Interface => class extends IndicatorsInterface( return `${input}`; } const invalidTimestamp = () => `${translate('Invalid timestamp')}: ${timestamp}`; - if(typeof timestamp === 'number') { + if (typeof timestamp === 'number') { const dateTime = new Date(timestamp * 1000); - if(dateTime.getTime()) { + if (dateTime.getTime()) { const year = dateTime.getFullYear(); const month = getTwoDigitValue(dateTime.getMonth() + 1); const day = getTwoDigitValue(dateTime.getDate()); @@ -37,12 +37,22 @@ export default Interface => class extends IndicatorsInterface( return `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${formatGTMoffset()}`; } return invalidTimestamp(); - } - return invalidTimestamp(); + } + return invalidTimestamp(); }, toTimestamp: (dateTimeString) => { - const date = new Date(dateTimeString.substr(0,19)).getTime(); - return date ? Math.floor(date / 1000) : `${translate('Invalid date/time')}: ${dateTimeString}`; + const invalidDatetime = () => `${translate('Invalid date/time')}: ${dateTimeString}`; + if (typeof dateTimeString === 'string') { + const tmp = dateTimeString.replace(/\s+/g, 'T').substr(0, 19); + const dateTime = tmp[tmp.length - 1] === 'T' ? tmp.substr(0, tmp.length - 1) : tmp; + const p = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])(T(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?)?)/; + if (p.test(dateTime)) { + const date = new Date(dateTime); + return date ? date.getTime() / 1000 : invalidDatetime(); + } + return invalidDatetime(); + } + return invalidDatetime(); }, ...this.getCandleInterface(), ...this.getMiscInterface(), From dd57fa1e8e7e13a085f685a46208512a214732d5 Mon Sep 17 00:00:00 2001 From: McSam Date: Mon, 24 Jun 2019 13:36:08 +0800 Subject: [PATCH 27/49] resolve issues by QA --- src/botPage/view/blockly/index.js | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index 9c3981bd33..e951568af6 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -331,6 +331,21 @@ export default class _Blockly { try { xml = Blockly.Xml.textToDom(blockStr); + const blocklyXml = xml.querySelectorAll('block'); + + if (blocklyXml.length <= 0) { + globalObserver.emit('ui.log.warn', `${translate('Unrecognized file format')}`); + return; + } + + blocklyXml.forEach(block => { + const blockType = block.attributes.type.nodeValue; + + if (!Object.keys(Blockly.Blocks).includes(blockType)) { + globalObserver.emit('ui.log.warn', `${translate('Unrecognized file format')}`); + throw createError('FileLoad', translate('Unrecognized file format')); + } + }); } catch (e) { throw createError('FileLoad', translate('Unrecognized file format')); } From f31771d81c0abc3c0fe8d96dd0c3e153d3d3bfaa Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 24 Jun 2019 18:17:26 +0800 Subject: [PATCH 28/49] fixed date object validation --- src/botPage/bot/Interface/ToolsInterface.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index f051a15c97..efd34fe159 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -48,8 +48,11 @@ export default Interface => class extends IndicatorsInterface( const p = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])(T(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?)?)/; if (p.test(dateTime)) { const date = new Date(dateTime); - return date ? date.getTime() / 1000 : invalidDatetime(); + // return `ok: ${ date}`; + // eslint-disable-next-line no-restricted-globals + return date instanceof Date && !isNaN(date) ? date.getTime() / 1000 : invalidDatetime(); } + // return `not ok: ${ dateTime}`; return invalidDatetime(); } return invalidDatetime(); From 84ff2c5b2d64b1ce1a25bc584c4913ae00a95be1 Mon Sep 17 00:00:00 2001 From: Anton Date: Mon, 24 Jun 2019 18:21:35 +0800 Subject: [PATCH 29/49] removed comments --- src/botPage/bot/Interface/ToolsInterface.js | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index efd34fe159..f9808c6022 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -48,11 +48,9 @@ export default Interface => class extends IndicatorsInterface( const p = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])(T(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?)?)/; if (p.test(dateTime)) { const date = new Date(dateTime); - // return `ok: ${ date}`; // eslint-disable-next-line no-restricted-globals return date instanceof Date && !isNaN(date) ? date.getTime() / 1000 : invalidDatetime(); } - // return `not ok: ${ dateTime}`; return invalidDatetime(); } return invalidDatetime(); From 20001e7ca8cdc1feb6d60517d3aa2cc75c9cce8c Mon Sep 17 00:00:00 2001 From: McSam Date: Wed, 26 Jun 2019 10:28:23 +0800 Subject: [PATCH 30/49] resolve xxs attack --- src/indexPage/endpoint.js | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/indexPage/endpoint.js b/src/indexPage/endpoint.js index 7032919acd..7f98b78923 100644 --- a/src/indexPage/endpoint.js +++ b/src/indexPage/endpoint.js @@ -20,6 +20,8 @@ export default function endpoint() { $('#new_endpoint').click(addEndpoint); $('#reset').click(resetEndpoint); + $('#server_url').keyup(validateURL); + init(); }); return true; @@ -64,6 +66,15 @@ function addEndpoint(e) { setStorage('config.server_url', serverUrl); setStorage('config.app_id', appId); + const UrlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/; + + if (!UrlReg.test(serverUrl)) { + $('#error') + .html('Please enter a valid server URL') + .show(); + return; + } + checkConnection(appId, serverUrl); } @@ -71,3 +82,5 @@ function resetEndpoint() { setStorage('config.app_id', getDefaultEndpoint().appId); setStorage('config.server_url', getDefaultEndpoint().url); } + +const validateURL = e => {}; From ba04cd37fae3b7e754514b622e78a5fa818be9ad Mon Sep 17 00:00:00 2001 From: McSam Date: Wed, 26 Jun 2019 10:32:15 +0800 Subject: [PATCH 31/49] eslint --- src/indexPage/endpoint.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/indexPage/endpoint.js b/src/indexPage/endpoint.js index 7f98b78923..6821af9a10 100644 --- a/src/indexPage/endpoint.js +++ b/src/indexPage/endpoint.js @@ -20,8 +20,6 @@ export default function endpoint() { $('#new_endpoint').click(addEndpoint); $('#reset').click(resetEndpoint); - $('#server_url').keyup(validateURL); - init(); }); return true; @@ -66,6 +64,7 @@ function addEndpoint(e) { setStorage('config.server_url', serverUrl); setStorage('config.app_id', appId); + // eslint-disable-next-line no-useless-escape const UrlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/; if (!UrlReg.test(serverUrl)) { @@ -82,5 +81,3 @@ function resetEndpoint() { setStorage('config.app_id', getDefaultEndpoint().appId); setStorage('config.server_url', getDefaultEndpoint().url); } - -const validateURL = e => {}; From 4dbf2a385377998b8b4498be201c466fdf58dd22 Mon Sep 17 00:00:00 2001 From: McSam Date: Wed, 26 Jun 2019 11:03:26 +0800 Subject: [PATCH 32/49] resolve changes --- src/indexPage/endpoint.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/indexPage/endpoint.js b/src/indexPage/endpoint.js index 6821af9a10..a657fbca78 100644 --- a/src/indexPage/endpoint.js +++ b/src/indexPage/endpoint.js @@ -1,5 +1,6 @@ import { get as getStorage, set as setStorage } from '../common/utils/storageManager'; import { generateWebSocketURL, getDefaultEndpoint, generateTestLiveApiInstance } from '../common/appId'; +import { translate } from '../common/utils/tools'; if (document.location.href.endsWith('/endpoint')) { window.location.replace(`${document.location.href}.html`); @@ -64,12 +65,11 @@ function addEndpoint(e) { setStorage('config.server_url', serverUrl); setStorage('config.app_id', appId); - // eslint-disable-next-line no-useless-escape - const UrlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:\.[\w\.-]+)+[\w\-\._~:/?#[\]@!\$&'\(\)\*\+,;=.]+$/; + const UrlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/; if (!UrlReg.test(serverUrl)) { $('#error') - .html('Please enter a valid server URL') + .html(translate('Please enter a valid server URL')) .show(); return; } From 358e93b2a68e1ff279163a364397d466c607cede Mon Sep 17 00:00:00 2001 From: McSam Date: Wed, 26 Jun 2019 13:03:24 +0800 Subject: [PATCH 33/49] eslint --- src/indexPage/endpoint.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/indexPage/endpoint.js b/src/indexPage/endpoint.js index a657fbca78..751699692f 100644 --- a/src/indexPage/endpoint.js +++ b/src/indexPage/endpoint.js @@ -65,9 +65,9 @@ function addEndpoint(e) { setStorage('config.server_url', serverUrl); setStorage('config.app_id', appId); - const UrlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:.[\w.-]+)+[\w\-._~:/?#[\]@!$&'()*+,;=.]+$/; + const urlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:.[\w.-]+)+[\w-._~:/?#[\]@!$&'()*+,;=.]+$/; - if (!UrlReg.test(serverUrl)) { + if (!urlReg.test(serverUrl)) { $('#error') .html(translate('Please enter a valid server URL')) .show(); From d0276a25ce9ca5e4bb0259e90d6754b57afa5798 Mon Sep 17 00:00:00 2001 From: McSam Date: Wed, 26 Jun 2019 15:19:50 +0800 Subject: [PATCH 34/49] regex --- src/indexPage/endpoint.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/indexPage/endpoint.js b/src/indexPage/endpoint.js index 751699692f..872d037d90 100644 --- a/src/indexPage/endpoint.js +++ b/src/indexPage/endpoint.js @@ -65,7 +65,7 @@ function addEndpoint(e) { setStorage('config.server_url', serverUrl); setStorage('config.app_id', appId); - const urlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:.[\w.-]+)+[\w-._~:/?#[\]@!$&'()*+,;=.]+$/; + const urlReg = /^(?:http(s)?:\/\/)?[\w.-]+(?:.[\w.-]+)+[\w-._~:\/?#[\]@!$&'()*+,;=.]+$/; if (!urlReg.test(serverUrl)) { $('#error') From adfd1a3e598934999ba7181ad21b56605f243f10 Mon Sep 17 00:00:00 2001 From: McSam Date: Wed, 26 Jun 2019 16:04:22 +0800 Subject: [PATCH 35/49] copywriters --- src/botPage/view/blockly/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index e951568af6..25e501f3ce 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -334,7 +334,12 @@ export default class _Blockly { const blocklyXml = xml.querySelectorAll('block'); if (blocklyXml.length <= 0) { - globalObserver.emit('ui.log.warn', `${translate('Unrecognized file format')}`); + globalObserver.emit( + 'ui.log.warn', + `${translate( + 'Unsupported strategy. Binary Bot only accepts XML scripts from Binary.com sources. Please load a supported file.' + )}` + ); return; } From e179a45ebbea8e182a39caf35dfca884c105d8c6 Mon Sep 17 00:00:00 2001 From: Anton Date: Thu, 27 Jun 2019 09:43:38 +0800 Subject: [PATCH 36/49] added more flexible validation --- src/botPage/bot/Interface/ToolsInterface.js | 30 ++++++++++++++++----- 1 file changed, 24 insertions(+), 6 deletions(-) diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index f9808c6022..1dfc0a3c2b 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -43,13 +43,31 @@ export default Interface => class extends IndicatorsInterface( toTimestamp: (dateTimeString) => { const invalidDatetime = () => `${translate('Invalid date/time')}: ${dateTimeString}`; if (typeof dateTimeString === 'string') { - const tmp = dateTimeString.replace(/\s+/g, 'T').substr(0, 19); - const dateTime = tmp[tmp.length - 1] === 'T' ? tmp.substr(0, tmp.length - 1) : tmp; - const p = /^([12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])(T(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9]))?)?)/; - if (p.test(dateTime)) { - const date = new Date(dateTime); + const dateTime = dateTimeString + .replace(/[^0-9.:-\s]/g, '') + .replace(/\s+/g,' ') + .replace(/\s$/,'') + .split(' '); + + const d = /^[12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/; + const t = /^(0[0-9]|1[0-9]|2[0-3]):([0-5][0-9])(:([0-5][0-9])?)?$/; + + let validatedDateTime; + + if(dateTime.length >= 2) { + validatedDateTime = d.test(dateTime[0]) && t.test(dateTime[1]) ? `${dateTime[0]}T${dateTime[1]}` : null; + } else if(dateTime.length === 1) { + validatedDateTime = d.test(dateTime[0]) ? dateTime[0] : null; + } else { + validatedDateTime = null; + } + + if(validatedDateTime) { + const dateObj = new Date(validatedDateTime); // eslint-disable-next-line no-restricted-globals - return date instanceof Date && !isNaN(date) ? date.getTime() / 1000 : invalidDatetime(); + if(dateObj instanceof Date && !isNaN(dateObj)) { + return dateObj.getTime() / 1000; + } } return invalidDatetime(); } From 47d1bf0fa16cbcc777fd53bcfa30612c7c374a18 Mon Sep 17 00:00:00 2001 From: McSam Date: Thu, 27 Jun 2019 15:07:54 +0800 Subject: [PATCH 37/49] resolve changes --- src/botPage/bot/TradeEngine/Total.js | 2 +- src/botPage/bot/TradeEngine/index.js | 2 +- src/botPage/bot/sanitize.js | 2 +- src/botPage/common/error.js | 10 ++++- src/botPage/view/blockly/index.js | 55 ++++++++++++++-------------- 5 files changed, 38 insertions(+), 33 deletions(-) diff --git a/src/botPage/bot/TradeEngine/Total.js b/src/botPage/bot/TradeEngine/Total.js index 355c7c6446..64a567e559 100644 --- a/src/botPage/bot/TradeEngine/Total.js +++ b/src/botPage/bot/TradeEngine/Total.js @@ -1,7 +1,7 @@ import { translate } from '../../../common/i18n'; import { roundBalance } from '../../common/tools'; import { info, notify } from '../broadcast'; -import createError from '../../common/error'; +import { createError } from '../../common/error'; import { observer as globalObserver } from '../../../common/utils/observer'; const skeleton = { diff --git a/src/botPage/bot/TradeEngine/index.js b/src/botPage/bot/TradeEngine/index.js index 9af841a0b8..aaec4f3c65 100644 --- a/src/botPage/bot/TradeEngine/index.js +++ b/src/botPage/bot/TradeEngine/index.js @@ -3,7 +3,7 @@ import { createStore, applyMiddleware } from 'redux'; import thunk from 'redux-thunk'; import { durationToSecond } from '../../../common/utils/tools'; import { translate } from '../../..//common/i18n'; -import createError from '../../common/error'; +import { createError } from '../../common/error'; import { doUntilDone } from '../tools'; import { expectInitArg, expectTradeOptions } from '../sanitize'; import Proposal from './Proposal'; diff --git a/src/botPage/bot/sanitize.js b/src/botPage/bot/sanitize.js index e5e07b6525..26c5244b8c 100644 --- a/src/botPage/bot/sanitize.js +++ b/src/botPage/bot/sanitize.js @@ -1,5 +1,5 @@ import { translate } from '../../common/i18n'; -import createError from '../common/error'; +import { createError } from '../common/error'; const isPositiveNumber = num => Number.isFinite(num) && num > 0; diff --git a/src/botPage/common/error.js b/src/botPage/common/error.js index 50ba49bf20..69458cfdb2 100644 --- a/src/botPage/common/error.js +++ b/src/botPage/common/error.js @@ -1,7 +1,13 @@ -const createError = (name, message) => { +import { observer as globalObserver } from '../../common/utils/observer'; +import { translate } from '../../common/i18n'; + +export const createError = (name, message) => { const e = new Error(message); e.name = name; return e; }; -export default createError; +export const createErrorAndEmit = (name, message) => { + globalObserver.emit('ui.log.warn', `${translate(message)}`); + return createError(name, message); +}; diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index 25e501f3ce..cac7853545 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -16,7 +16,7 @@ import { cleanBeforeExport, } from './utils'; import Interpreter from '../../bot/Interpreter'; -import createError from '../../common/error'; +import { createErrorAndEmit } from '../../common/error'; import { translate, xml as translateXml } from '../../../common/i18n'; import { getLanguage } from '../../../common/lang'; import { observer as globalObserver } from '../../../common/utils/observer'; @@ -315,46 +315,45 @@ export default class _Blockly { } /* eslint-disable class-methods-use-this */ load(blockStr = '', dropEvent = {}) { - let xml; + const unrecognisedMsg = () => translate('Unrecognized file format'); try { const xmlDoc = new DOMParser().parseFromString(blockStr, 'application/xml'); - if (xmlDoc.getElementsByTagName('parsererror').length > 0) { - globalObserver.emit('ui.log.warn', `${translate('Unrecognized file format')}`); - return; + if (xmlDoc.getElementsByTagName('parsererror').length) { + throw 0; } } catch (err) { - globalObserver.emit('ui.log.warn', `${translate('Unrecognized file format')}`); - return; + throw createErrorAndEmit('FileLoad', unrecognisedMsg()); } + let xml; try { xml = Blockly.Xml.textToDom(blockStr); - const blocklyXml = xml.querySelectorAll('block'); - - if (blocklyXml.length <= 0) { - globalObserver.emit( - 'ui.log.warn', - `${translate( - 'Unsupported strategy. Binary Bot only accepts XML scripts from Binary.com sources. Please load a supported file.' - )}` - ); - return; - } + } catch (e) { + throw createErrorAndEmit('FileLoad', unrecognisedMsg()); + } - blocklyXml.forEach(block => { - const blockType = block.attributes.type.nodeValue; + const blocklyXml = xml.querySelectorAll('block'); - if (!Object.keys(Blockly.Blocks).includes(blockType)) { - globalObserver.emit('ui.log.warn', `${translate('Unrecognized file format')}`); - throw createError('FileLoad', translate('Unrecognized file format')); - } - }); - } catch (e) { - throw createError('FileLoad', translate('Unrecognized file format')); + if (!blocklyXml.length) { + throw createErrorAndEmit( + 'FileLoad', + 'XML file contains unsupported elements. Please check or modify file.' + ); } + blocklyXml.forEach(block => { + const blockType = block.attributes.type.nodeValue; + + if (!Object.keys(Blockly.Blocks).includes(blockType)) { + throw createErrorAndEmit( + 'FileLoad', + 'XML file contains unsupported elements. Please check or modify file' + ); + } + }); + try { if (xml.hasAttribute('collection') && xml.getAttribute('collection') === 'true') { loadBlocks(xml, dropEvent); @@ -362,7 +361,7 @@ export default class _Blockly { loadWorkspace(xml); } } catch (e) { - throw createError('FileLoad', translate('Unable to load the block file')); + throw createErrorAndEmit('FileLoad', translate('Unable to load the block file')); } } /* eslint-disable class-methods-use-this */ From c31b133ec8ba23067dd2a0f9327a4f3db177a8c0 Mon Sep 17 00:00:00 2001 From: McSam Date: Thu, 27 Jun 2019 15:13:37 +0800 Subject: [PATCH 38/49] eslint & resolve changes --- src/botPage/view/blockly/index.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index cac7853545..6e35a889c0 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -321,7 +321,7 @@ export default class _Blockly { const xmlDoc = new DOMParser().parseFromString(blockStr, 'application/xml'); if (xmlDoc.getElementsByTagName('parsererror').length) { - throw 0; + throw new Error(); } } catch (err) { throw createErrorAndEmit('FileLoad', unrecognisedMsg()); @@ -344,7 +344,7 @@ export default class _Blockly { } blocklyXml.forEach(block => { - const blockType = block.attributes.type.nodeValue; + const blockType = block.getAttribute('type'); if (!Object.keys(Blockly.Blocks).includes(blockType)) { throw createErrorAndEmit( From 2992b40a3715c0f31f9f356517e6dfc70b01cbe0 Mon Sep 17 00:00:00 2001 From: Anton Date: Thu, 27 Jun 2019 16:07:15 +0800 Subject: [PATCH 39/49] trim spaces in the beginning/end of date-time string --- src/botPage/bot/Interface/ToolsInterface.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/botPage/bot/Interface/ToolsInterface.js b/src/botPage/bot/Interface/ToolsInterface.js index 1dfc0a3c2b..2067f0889b 100644 --- a/src/botPage/bot/Interface/ToolsInterface.js +++ b/src/botPage/bot/Interface/ToolsInterface.js @@ -46,7 +46,7 @@ export default Interface => class extends IndicatorsInterface( const dateTime = dateTimeString .replace(/[^0-9.:-\s]/g, '') .replace(/\s+/g,' ') - .replace(/\s$/,'') + .trim() .split(' '); const d = /^[12]\d{3}-(0[1-9]|1[0-2])-(0[1-9]|[12]\d|3[01])$/; From a4a5404f6c7ddb129ef0e74fe6bf9fcabb59e830 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 2 Jul 2019 11:00:38 +0800 Subject: [PATCH 40/49] Set stale.yml config --- .github/stale.yml | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 .github/stale.yml diff --git a/.github/stale.yml b/.github/stale.yml new file mode 100644 index 0000000000..58c238a3ff --- /dev/null +++ b/.github/stale.yml @@ -0,0 +1,24 @@ +# Configuration for probot-stale - https://github.com/probot/stale + +# Limit to only `issues` +only: issues + +# Number of days of inactivity before an Issue or Pull Request is closed. +# Set to false to disable. If disabled, issues still need to be closed manually, but will remain marked as stale. +daysUntilClose: 14 + +# Issues or Pull Requests with these labels will never be considered stale. Set to `[]` to disable +exemptLabels: + - bug + - "technical issues" + +# Comment to post when marking as stale. Set to `false` to disable +markComment: false + +# Comment to post when closing a stale Issue or Pull Request. +closeComment: > + This issue has been automatically closed since there has not been + any recent activity. Please open a new issue for related bugs. + +# Limit the number of actions per hour, from 1-30. Default is 30 +limitPerRun: 30 \ No newline at end of file From e0022d95adccacb1e6c69605ae3c0d10d08a6ea1 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 11:46:27 +0800 Subject: [PATCH 41/49] Keep track of bot is_running status globally --- src/botPage/bot/Interpreter.js | 4 +++- src/botPage/bot/TradeEngine/index.js | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/src/botPage/bot/Interpreter.js b/src/botPage/bot/Interpreter.js index 57854d4428..f7e13d06ae 100644 --- a/src/botPage/bot/Interpreter.js +++ b/src/botPage/bot/Interpreter.js @@ -149,8 +149,10 @@ export default class Interpreter { } terminateSession() { this.$scope.api.disconnect(); - globalObserver.emit('bot.stop'); this.stopped = true; + + globalObserver.emit('bot.stop'); + globalObserver.setState({ is_running: false }); } stop() { if (this.bot.tradeEngine.isSold === false && !this.isErrorTriggered) { diff --git a/src/botPage/bot/TradeEngine/index.js b/src/botPage/bot/TradeEngine/index.js index aaec4f3c65..015b215d1f 100644 --- a/src/botPage/bot/TradeEngine/index.js +++ b/src/botPage/bot/TradeEngine/index.js @@ -92,6 +92,7 @@ export default class TradeEngine extends Balance(Purchase(Sell(OpenContract(Prop } globalObserver.emit('bot.running'); + globalObserver.setState({ is_running: true }); this.tradeOptions = expectTradeOptions(tradeOptions); From 4527c2d001a83c5a17d188f07bb43b7448013046 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 11:48:33 +0800 Subject: [PATCH 42/49] Ensure correct button status --- src/botPage/view/View.js | 52 ++++++++++++++++++++++++++++------------ 1 file changed, 37 insertions(+), 15 deletions(-) diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index 690a9552ae..4788e81aba 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -511,11 +511,17 @@ export default class View { }); const startBot = limitations => { - const $runButtons = $('#runButton, #summaryRunButton'); - const $stopButtons = $('#stopButton, #summaryStopButton'); - $stopButtons.show(); - $runButtons.hide(); - $runButtons.prop('disabled', true); + const el_run_buttons = document.querySelectorAll('#runButton, #summaryRunButton'); + const el_stop_buttons = document.querySelectorAll('#stopButton, #summaryStopButton'); + + el_run_buttons.forEach(el_run_button => { + el_run_button.style.display = 'none'; + el_run_button.setAttributeNode(document.createAttribute('disabled')); + }); + el_stop_buttons.forEach(el_stop_button => { + el_stop_button.style.display = 'initial'; + }); + globalObserver.emit('summary.disable_clear'); showSummary(); this.blockly.run(limitations); @@ -627,6 +633,9 @@ export default class View { this.blockly.stop(); } addEventHandlers() { + const getRunButtonElements = () => document.querySelectorAll('#runButton, #summaryRunButton'); + const getStopButtonElements = () => document.querySelectorAll('#stopButton, #summaryStopButton'); + window.addEventListener('storage', e => { window.onbeforeunload = null; if (e.key === 'activeToken' && !e.newValue) window.location.reload(); @@ -634,7 +643,8 @@ export default class View { }); globalObserver.register('Error', error => { - $('#runButton, #summaryRunButton').prop('disabled', false); + getRunButtonElements().forEach(el_run_button => el_run_button.removeAttribute('disabled')); + if (error.error && error.error.error.code === 'InvalidToken') { removeAllTokens(); updateTokenList(); @@ -642,16 +652,28 @@ export default class View { } }); + globalObserver.register('bot.running', () => { + getRunButtonElements().forEach(el_run_button => { + el_run_button.style.display = 'none'; + el_run_button.setAttributeNode(document.createAttribute('disabled')); + }); + getStopButtonElements().forEach(el_stop_button => { + el_stop_button.style.display = 'inline-block'; + el_stop_button.removeAttribute('disabled'); + }); + }); + globalObserver.register('bot.stop', () => { - const $runButtons = $('#runButton, #summaryRunButton'); - const $stopButtons = $('#stopButton, #summaryStopButton'); - if ($runButtons.is(':visible') || $stopButtons.is(':visible')) { - $runButtons.show(); - $stopButtons.hide(); - - $stopButtons.prop('disabled', false); - $runButtons.prop('disabled', false); - } + // Enable run button, this event is emitted after the interpreter + // killed the API connection. + getStopButtonElements().forEach(el_stop_button => { + el_stop_button.style.display = 'none'; + el_stop_button.removeAttribute('disabled'); + }); + getRunButtonElements().forEach(el_run_button => { + el_run_button.style.display = null; + el_run_button.removeAttribute('disabled'); + }); }); globalObserver.register('bot.info', info => { From f5cc80cfddf0933b5a7cc125be395c5433b803da Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 11:50:26 +0800 Subject: [PATCH 43/49] Refactor disableRunButton function --- src/botPage/view/blockly/blocks/shared.js | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/src/botPage/view/blockly/blocks/shared.js b/src/botPage/view/blockly/blocks/shared.js index 80406ad6e2..cf16369302 100644 --- a/src/botPage/view/blockly/blocks/shared.js +++ b/src/botPage/view/blockly/blocks/shared.js @@ -410,6 +410,21 @@ export const getPredictionForContracts = (contracts, selectedContractType) => { return predictionRange; }; -export const disableRunButton = isDisabled => { - $('#runButton, #summaryRunButton').attr('disabled', isDisabled); +export const disableRunButton = should_disable => { + const el_run_buttons = document.querySelectorAll('#runButton, #summaryRunButton'); + const is_running = globalObserver.getState('is_running'); + + el_run_buttons.forEach(el_run_button => { + if (is_running) { + if (should_disable) { + el_run_button.setAttributeNode(document.createAttribute('disabled')); + } else { + // Do not enable. The bot is running. + } + } else if (should_disable) { + el_run_button.setAttributeNode(document.createAttribute('disabled')); + } else { + el_run_button.removeAttribute('disabled'); + } + }); }; From 280a90224c4d02b5115db8412913e80bfe0dc774 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 11:52:32 +0800 Subject: [PATCH 44/49] Restore display on stopBeforeStart --- src/botPage/view/blockly/index.js | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index f5d9c60fd4..880251f950 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -440,7 +440,11 @@ while(true) { } stop(stopBeforeStart) { if (!stopBeforeStart) { - $('#stopButton, #summaryStopButton').prop('disabled', true); + const el_run_buttons = document.querySelectorAll('#runButton, #summaryRunButton'); + const el_stop_buttons = document.querySelectorAll('#stopButton, #summaryStopButton'); + + el_run_buttons.forEach(el_run_button => (el_run_button.style.display = 'initial')); + el_stop_buttons.forEach(el_stop_button => (el_stop_button.style.display = 'none')); } if (this.interpreter) { this.interpreter.stop(); From e550184705cf87111ae056b3f55c496669caccdb Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 12:00:02 +0800 Subject: [PATCH 45/49] Convert everything to camelCase --- src/botPage/bot/Interpreter.js | 2 +- src/botPage/bot/TradeEngine/index.js | 2 +- src/botPage/view/View.js | 46 +++++++++++++---------- src/botPage/view/blockly/blocks/shared.js | 22 +++++------ src/botPage/view/blockly/index.js | 14 +++++-- 5 files changed, 49 insertions(+), 37 deletions(-) diff --git a/src/botPage/bot/Interpreter.js b/src/botPage/bot/Interpreter.js index f7e13d06ae..1f0959e5a1 100644 --- a/src/botPage/bot/Interpreter.js +++ b/src/botPage/bot/Interpreter.js @@ -152,7 +152,7 @@ export default class Interpreter { this.stopped = true; globalObserver.emit('bot.stop'); - globalObserver.setState({ is_running: false }); + globalObserver.setState({ isRunning: false }); } stop() { if (this.bot.tradeEngine.isSold === false && !this.isErrorTriggered) { diff --git a/src/botPage/bot/TradeEngine/index.js b/src/botPage/bot/TradeEngine/index.js index 015b215d1f..8ad3f62180 100644 --- a/src/botPage/bot/TradeEngine/index.js +++ b/src/botPage/bot/TradeEngine/index.js @@ -92,7 +92,7 @@ export default class TradeEngine extends Balance(Purchase(Sell(OpenContract(Prop } globalObserver.emit('bot.running'); - globalObserver.setState({ is_running: true }); + globalObserver.setState({ isRunning: true }); this.tradeOptions = expectTradeOptions(tradeOptions); diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index 4788e81aba..bae088a63c 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -511,15 +511,17 @@ export default class View { }); const startBot = limitations => { - const el_run_buttons = document.querySelectorAll('#runButton, #summaryRunButton'); - const el_stop_buttons = document.querySelectorAll('#stopButton, #summaryStopButton'); + const elRunButtons = document.querySelectorAll('#runButton, #summaryRunButton'); + const elStopButtons = document.querySelectorAll('#stopButton, #summaryStopButton'); - el_run_buttons.forEach(el_run_button => { - el_run_button.style.display = 'none'; - el_run_button.setAttributeNode(document.createAttribute('disabled')); + elRunButtons.forEach(el => { + const elRunButton = el; + elRunButton.style.display = 'none'; + elRunButton.setAttributeNode(document.createAttribute('disabled')); }); - el_stop_buttons.forEach(el_stop_button => { - el_stop_button.style.display = 'initial'; + elStopButtons.forEach(el => { + const elStopButton = el; + elStopButton.style.display = 'initial'; }); globalObserver.emit('summary.disable_clear'); @@ -643,7 +645,7 @@ export default class View { }); globalObserver.register('Error', error => { - getRunButtonElements().forEach(el_run_button => el_run_button.removeAttribute('disabled')); + getRunButtonElements().forEach(elRunButton => elRunButton.removeAttribute('disabled')); if (error.error && error.error.error.code === 'InvalidToken') { removeAllTokens(); @@ -653,26 +655,30 @@ export default class View { }); globalObserver.register('bot.running', () => { - getRunButtonElements().forEach(el_run_button => { - el_run_button.style.display = 'none'; - el_run_button.setAttributeNode(document.createAttribute('disabled')); + getRunButtonElements().forEach(el => { + const elRunButton = el; + elRunButton.style.display = 'none'; + elRunButton.setAttributeNode(document.createAttribute('disabled')); }); - getStopButtonElements().forEach(el_stop_button => { - el_stop_button.style.display = 'inline-block'; - el_stop_button.removeAttribute('disabled'); + getStopButtonElements().forEach(el => { + const elStopButton = el; + elStopButton.style.display = 'inline-block'; + elStopButton.removeAttribute('disabled'); }); }); globalObserver.register('bot.stop', () => { // Enable run button, this event is emitted after the interpreter // killed the API connection. - getStopButtonElements().forEach(el_stop_button => { - el_stop_button.style.display = 'none'; - el_stop_button.removeAttribute('disabled'); + getStopButtonElements().forEach(el => { + const elStopButton = el; + elStopButton.style.display = 'none'; + elStopButton.removeAttribute('disabled'); }); - getRunButtonElements().forEach(el_run_button => { - el_run_button.style.display = null; - el_run_button.removeAttribute('disabled'); + getRunButtonElements().forEach(el => { + const elRunButton = el; + elRunButton.style.display = null; + elRunButton.removeAttribute('disabled'); }); }); diff --git a/src/botPage/view/blockly/blocks/shared.js b/src/botPage/view/blockly/blocks/shared.js index cf16369302..7e5ed841fe 100644 --- a/src/botPage/view/blockly/blocks/shared.js +++ b/src/botPage/view/blockly/blocks/shared.js @@ -410,21 +410,21 @@ export const getPredictionForContracts = (contracts, selectedContractType) => { return predictionRange; }; -export const disableRunButton = should_disable => { - const el_run_buttons = document.querySelectorAll('#runButton, #summaryRunButton'); - const is_running = globalObserver.getState('is_running'); - - el_run_buttons.forEach(el_run_button => { - if (is_running) { - if (should_disable) { - el_run_button.setAttributeNode(document.createAttribute('disabled')); +export const disableRunButton = shouldDisable => { + const elRunButtons = document.querySelectorAll('#runButton, #summaryRunButton'); + const isRunning = globalObserver.getState('isRunning'); + + elRunButtons.forEach(elRunButton => { + if (isRunning) { + if (shouldDisable) { + elRunButton.setAttributeNode(document.createAttribute('disabled')); } else { // Do not enable. The bot is running. } - } else if (should_disable) { - el_run_button.setAttributeNode(document.createAttribute('disabled')); + } else if (shouldDisable) { + elRunButton.setAttributeNode(document.createAttribute('disabled')); } else { - el_run_button.removeAttribute('disabled'); + elRunButton.removeAttribute('disabled'); } }); }; diff --git a/src/botPage/view/blockly/index.js b/src/botPage/view/blockly/index.js index 880251f950..cf92dd5e2c 100644 --- a/src/botPage/view/blockly/index.js +++ b/src/botPage/view/blockly/index.js @@ -440,11 +440,17 @@ while(true) { } stop(stopBeforeStart) { if (!stopBeforeStart) { - const el_run_buttons = document.querySelectorAll('#runButton, #summaryRunButton'); - const el_stop_buttons = document.querySelectorAll('#stopButton, #summaryStopButton'); + const elRunButtons = document.querySelectorAll('#runButton, #summaryRunButton'); + const elStopButtons = document.querySelectorAll('#stopButton, #summaryStopButton'); - el_run_buttons.forEach(el_run_button => (el_run_button.style.display = 'initial')); - el_stop_buttons.forEach(el_stop_button => (el_stop_button.style.display = 'none')); + elRunButtons.forEach(el => { + const elRunButton = el; + elRunButton.style.display = 'initial'; + }); + elStopButtons.forEach(el => { + const elStopButton = el; + elStopButton.style.display = null; + }); } if (this.interpreter) { this.interpreter.stop(); From b603e314cc9ef80dcf47be5ca91c863cc13d692f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 13:19:33 +0800 Subject: [PATCH 46/49] Don't manipulate passed argument directly --- src/botPage/view/View.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index bae088a63c..3f5ed7f976 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -645,7 +645,10 @@ export default class View { }); globalObserver.register('Error', error => { - getRunButtonElements().forEach(elRunButton => elRunButton.removeAttribute('disabled')); + getRunButtonElements().forEach(el => { + const elRunButton = el; + elRunButton.removeAttribute('disabled'); + }); if (error.error && error.error.error.code === 'InvalidToken') { removeAllTokens(); From 481305dc860cc78952f9991a847ce43fb89c977f Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 13:19:55 +0800 Subject: [PATCH 47/49] Set #summaryStopButton to display: none by default --- static/css/bot.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/css/bot.scss b/static/css/bot.scss index a4a2063f26..76111ace27 100644 --- a/static/css/bot.scss +++ b/static/css/bot.scss @@ -169,7 +169,7 @@ body { background: black; } -#stopButton { +#stopButton, #summaryStopButton { display: none; } From a2c80015ef3ec31812683c460ae7d8243f7e6ef0 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 13:35:13 +0800 Subject: [PATCH 48/49] Be consistent in display values --- src/botPage/view/View.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/botPage/view/View.js b/src/botPage/view/View.js index 3f5ed7f976..3000071b5f 100644 --- a/src/botPage/view/View.js +++ b/src/botPage/view/View.js @@ -521,7 +521,7 @@ export default class View { }); elStopButtons.forEach(el => { const elStopButton = el; - elStopButton.style.display = 'initial'; + elStopButton.style.display = 'inline-block'; }); globalObserver.emit('summary.disable_clear'); @@ -675,7 +675,7 @@ export default class View { // killed the API connection. getStopButtonElements().forEach(el => { const elStopButton = el; - elStopButton.style.display = 'none'; + elStopButton.style.display = null; elStopButton.removeAttribute('disabled'); }); getRunButtonElements().forEach(el => { From a62ebcae910c5034cec6b39c87a95b3fa7c4ebf3 Mon Sep 17 00:00:00 2001 From: Aaron Imming Date: Tue, 9 Jul 2019 11:44:53 +0800 Subject: [PATCH 49/49] Create global state in observer --- src/common/utils/observer.js | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/common/utils/observer.js b/src/common/utils/observer.js index 9fded6642a..d0f06d51f6 100644 --- a/src/common/utils/observer.js +++ b/src/common/utils/observer.js @@ -3,6 +3,7 @@ import { Map, List } from 'immutable'; export default class Observer { constructor() { this.eam = new Map(); // event action map + this.state = {}; } register(event, _action, once, unregisterIfError, unregisterAllBefore) { if (unregisterAllBefore) { @@ -53,6 +54,12 @@ export default class Observer { this.eam.get(event).forEach(action => action.action(data)); } } + setState(state = {}) { + this.state = Object.assign({}, this.state, state); + } + getState(key) { + return this.state[key]; + } } export const observer = new Observer();