diff --git a/Animation.js b/Animation.js index 01ce1f6f5..95bf8a85d 100644 --- a/Animation.js +++ b/Animation.js @@ -57,7 +57,7 @@ function newAnimation () { /* We set the canvas to its normal width and height */ browserCanvas.width = window.innerWidth - browserCanvas.height = window.innerHeight - window.canvasApp.topMargin + browserCanvas.height = window.innerHeight - CURRENT_TOP_MARGIN /* First thing is to clear the actual canvas */ diff --git a/Canvas.js b/Canvas.js index 33e8e77d4..19a14f60a 100644 --- a/Canvas.js +++ b/Canvas.js @@ -60,16 +60,15 @@ function newCanvas () { browserCanvas.removeEventListener('click', onMouseClick, false) browserCanvas.removeEventListener('mouseout', onMouseOut, false) - /* Mouse wheel events. */ - if (browserCanvas.removeEventListener) { - // IE9, Chrome, Safari, Opera - browserCanvas.removeEventListener('mousewheel', onMouseWheel, false) - // Firefox - browserCanvas.removeEventListener('DOMMouseScroll', onMouseWheel, false) - } - // IE 6/7/8 - else browserCanvas.detachEvent('onmousewheel', onMouseWheel) + browserCanvas.removeEventListener('mousewheel', onMouseWheel, false)// IE9, Chrome, Safari, Opera + browserCanvas.removeEventListener('DOMMouseScroll', onMouseWheel, false) // Firefox + } else browserCanvas.detachEvent('onmousewheel', onMouseWheel) // IE 6/7/8 + + browserCanvas.removeEventListener('dragenter', onDragEnter, false) + browserCanvas.removeEventListener('dragleave', onDragLeave, false) + browserCanvas.removeEventListener('dragover', onDragOver, false) + browserCanvas.removeEventListener('drop', onDragDrop, false) } catch (err) { if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) } } @@ -110,9 +109,6 @@ function newCanvas () { if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onCharSpaceInitialized -> err = ' + err.stack) } } } - - /* Splash Screen */ - splashScreen = newSplashScreen() splashScreen.initialize() @@ -121,8 +117,6 @@ function newCanvas () { thisObject.animation = animation - /* Here we add all the functions that will be called during the animation cycle. */ - animation.addCallBackFunction('Floating Space Draw', thisObject.floatingSpace.draw) animation.addCallBackFunction('Floating Space Physics', thisObject.floatingSpace.physics) animation.addCallBackFunction('Chart Space Background', thisObject.chartSpace.drawBackground) @@ -144,7 +138,6 @@ function newCanvas () { function initializeBrowserCanvas () { try { browserCanvasContext = browserCanvas.getContext('2d') - // browserCanvasContext.font = 'italic small-caps bold 12px Saira' browserCanvasContext.font = 'Saira' viewPort.initialize() @@ -155,24 +148,23 @@ function newCanvas () { function addCanvasEvents () { try { - /* Mouse down and up events to control the drag of the canvas. */ - browserCanvas.addEventListener('mousedown', onMouseDown, false) browserCanvas.addEventListener('mouseup', onMouseUp, false) browserCanvas.addEventListener('mousemove', onMouseMove, false) browserCanvas.addEventListener('click', onMouseClick, false) browserCanvas.addEventListener('mouseout', onMouseOut, false) - /* Mouse wheel events. */ - if (browserCanvas.addEventListener) { - // IE9, Chrome, Safari, Opera - browserCanvas.addEventListener('mousewheel', onMouseWheel, false) - // Firefox - browserCanvas.addEventListener('DOMMouseScroll', onMouseWheel, false) - } - // IE 6/7/8 - else browserCanvas.attachEvent('onmousewheel', onMouseWheel) + browserCanvas.addEventListener('mousewheel', onMouseWheel, false) // IE9, Chrome, Safari, Opera + browserCanvas.addEventListener('DOMMouseScroll', onMouseWheel, false) // Firefox + } else browserCanvas.attachEvent('onmousewheel', onMouseWheel)// IE 6/7/8 + + /* Dragging Files Over the Canvas */ + + browserCanvas.addEventListener('dragenter', onDragEnter, false) + browserCanvas.addEventListener('dragleave', onDragLeave, false) + browserCanvas.addEventListener('dragover', onDragOver, false) + browserCanvas.addEventListener('drop', onDragDrop, false) // Disables the context menu when you right mouse click the canvas. browserCanvas.oncontextmenu = function (e) { @@ -183,11 +175,67 @@ function newCanvas () { } } + function onDragEnter (event) { + try { + event.preventDefault() + event.stopPropagation() + thisObject.cockpitSpace.toTop() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragEnter -> err = ' + err.stack) } + } + } + + function onDragLeave (event) { + try { + event.preventDefault() + event.stopPropagation() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragLeave -> err = ' + err.stack) } + } + } + + function onDragOver (event) { + try { + event.preventDefault() + event.stopPropagation() + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragOver -> err = ' + err.stack) } + } + } + + function onDragDrop (event) { + try { + event.preventDefault() + event.stopPropagation() + + let files = event.dataTransfer.files + + handleFiles(files) + function handleFiles (files) { + ([...files]).forEach(loadFileData) + } + + function loadFileData (file) { + let reader = new FileReader() + reader.readAsText(file) + reader.onloadend = function () { + let mousePosition = { + x: event.x, + y: event.y + } + thisObject.strategySpace.workspace.spawn(reader.result, mousePosition) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] onDragDrop -> err = ' + err.stack) } + } + } + function onMouseDown (event) { try { let point = { x: event.pageX, - y: event.pageY - window.canvasApp.topMargin + y: event.pageY - CURRENT_TOP_MARGIN } dragVector.downX = point.x @@ -202,6 +250,7 @@ function newCanvas () { if (container !== undefined && container.isDraggeable === true) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -216,6 +265,7 @@ function newCanvas () { if (container !== undefined && container.isDraggeable === true) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -226,6 +276,12 @@ function newCanvas () { if (container !== undefined && container.isDraggeable === true) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) + return + } + + if (container !== undefined && container.isClickeable === true) { + /* We dont want to mess up with the click */ return } @@ -236,6 +292,7 @@ function newCanvas () { if (container !== undefined && container.isDraggeable === true && event.button === 2) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -252,6 +309,7 @@ function newCanvas () { if (container.isDraggeable === true) { containerBeingDragged = container containerDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } else { viewPortBeingDragged = true @@ -265,6 +323,7 @@ function newCanvas () { containerBeingDragged = container containerDragStarted = true floatingObjectDragStarted = true + containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point) return } @@ -285,7 +344,7 @@ function newCanvas () { } let point = { x: event.pageX, - y: event.pageY - window.canvasApp.topMargin + y: event.pageY - CURRENT_TOP_MARGIN } let container @@ -362,38 +421,11 @@ function newCanvas () { deactivateDragging(event) } - function deactivateDragging (event) { - try { - if (containerDragStarted || viewPortBeingDragged || floatingObjectDragStarted) { - thisObject.eventHandler.raiseEvent('Drag Finished', undefined) - } - - if ( - containerDragStarted || - floatingObjectDragStarted || - viewPortBeingDragged - ) { - ignoreNextClick = true - } - /* Turn off all the possible things that can be dragged. */ - - containerDragStarted = false - floatingObjectDragStarted = false - viewPortBeingDragged = false - - containerBeingDragged = undefined - - browserCanvas.style.cursor = 'auto' - } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] onMouseUp -> err = ' + err.stack) } - } - } - function onMouseMove (event) { try { let point = { x: event.pageX, - y: event.pageY - window.canvasApp.topMargin + y: event.pageY - CURRENT_TOP_MARGIN } viewPort.mousePosition.x = point.x @@ -404,6 +436,7 @@ function newCanvas () { let targetContainer = thisObject.floatingSpace.getContainer(point) if (targetContainer !== undefined) { if (targetContainer.id !== containerBeingDragged.id) { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', point) containerBeingDragged = undefined containerDragStarted = false floatingObjectDragStarted = false @@ -412,6 +445,7 @@ function newCanvas () { return } } else { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', point) containerDragStarted = false containerBeingDragged = undefined floatingObjectDragStarted = false @@ -439,7 +473,7 @@ function newCanvas () { let point = { x: event.pageX, - y: event.pageY - window.canvasApp.topMargin + y: event.pageY - CURRENT_TOP_MARGIN } let container @@ -534,7 +568,7 @@ function newCanvas () { let point = { x: event.pageX, - y: event.pageY - window.canvasApp.topMargin + y: event.pageY - CURRENT_TOP_MARGIN } event.mousePosition = point @@ -583,12 +617,42 @@ function newCanvas () { } } + function deactivateDragging (event) { + try { + if (containerDragStarted || viewPortBeingDragged || floatingObjectDragStarted) { + thisObject.eventHandler.raiseEvent('Drag Finished', undefined) + } + + if ( + containerDragStarted || + floatingObjectDragStarted || + viewPortBeingDragged + ) { + ignoreNextClick = true + } + /* Turn off all the possible things that can be dragged. */ + + containerDragStarted = false + floatingObjectDragStarted = false + viewPortBeingDragged = false + + if (containerBeingDragged !== undefined) { + containerBeingDragged.eventHandler.raiseEvent('onDragFinished', event) + containerBeingDragged = undefined + } + + browserCanvas.style.cursor = 'auto' + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] deactivateDragging -> err = ' + err.stack) } + } + } + function checkDrag (event) { try { if (containerDragStarted === true || floatingObjectDragStarted === true || viewPortBeingDragged === true) { let point = { x: event.pageX, - y: event.pageY - window.canvasApp.topMargin + y: event.pageY - CURRENT_TOP_MARGIN } browserCanvas.style.cursor = 'grabbing' @@ -641,3 +705,4 @@ function newCanvas () { } } } + diff --git a/ChartsSpace/PlottersManager.js b/ChartsSpace/PlottersManager.js index dd693d6d6..47e5c71f7 100644 --- a/ChartsSpace/PlottersManager.js +++ b/ChartsSpace/PlottersManager.js @@ -1,7 +1,5 @@ function newPlottersManager () { const MODULE_NAME = 'Plotters Manager' - const INFO_LOG = false - const INTENSIVE_LOG = false const ERROR_LOG = true const logger = newWebDebugLog() logger.fileName = MODULE_NAME @@ -13,10 +11,11 @@ function newPlottersManager () { let datetime = NEW_SESSION_INITIAL_DATE let thisObject = { + fitFunction: undefined, + container: undefined, setDatetime: setDatetime, setTimePeriod: setTimePeriod, positionAtDatetime: positionAtDatetime, - container: undefined, draw: draw, getContainer: getContainer, initialize: initialize, @@ -36,57 +35,40 @@ function newPlottersManager () { function finalize (callBackFunction) { try { - if (INFO_LOG === true) { logger.write('[INFO] finalize -> Entering function.') } - for (let i = 0; i < productPlotters.length; i++) { if (productPlotters[i].profile !== undefined) { canvas.floatingSpace.profileBalls.destroyProfileBall(productPlotters[i].profile) } /* Destroyd the Note Set */ - canvas.floatingSpace.noteSets.destroyNoteSet(productPlotters[i].noteSet) - /* Then the panels. */ - for (let j = 0; j < productPlotters[i].panels.length; j++) { canvas.panelsSpace.destroyPanel(productPlotters[i].panels[j]) } - /* Finally the Storage Objects */ - productPlotters[i].storage.finalize() - if (productPlotters[i].plotter.finalize !== undefined) { productPlotters[i].plotter.finalize() } - productPlotters.splice(i, 1) // Delete item from array. } - for (let i = 0; i < competitionPlotters.length; i++) { if (competitionPlotters[i].profile !== undefined) { canvas.floatingSpace.profileBalls.destroyProfileBall(competitionPlotters[i].profile) } /* Destroyd the Note Set */ - canvas.floatingSpace.noteSets.destroyNoteSet(competitionPlotters[i].noteSet) - /* Then the panels. */ - if (competitionPlotters[i].panels !== undefined) { for (let j = 0; j < competitionPlotters[i].panels.length; j++) { canvas.panelsSpace.destroyPanel(competitionPlotters[i].panels[j]) } } - /* Finally the Storage Objects */ - competitionPlotters[i].storage.finalize() - if (competitionPlotters[i].plotter.finalize !== undefined) { competitionPlotters[i].plotter.finalize() } - competitionPlotters.splice(i, 1) // Delete item from array. } } catch (err) { @@ -97,38 +79,25 @@ function newPlottersManager () { function initialize (pProductsPanel, pExchange, pMarket, callBackFunction) { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> Entering function.') } - /* Remember the Products Panel */ - productsPanel = pProductsPanel exchange = pExchange market = pMarket - /* Listen to the event of change of status */ - productsPanel.container.eventHandler.listenToEvent('Product Card Status Changed', onProductCardStatusChanged) - initializeCompetitionPlotters(onCompetitionPlottersInitialized) function onCompetitionPlottersInitialized (err) { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onCompetitionPlottersInitialized -> Entering function.') } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onCompetitionPlottersInitialized -> Received OK Response.') } break } - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onCompetitionPlottersInitialized -> Received FAIL Response.') } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } - default: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileCursorReady -> Received Unexpected Response.') } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } @@ -138,25 +107,19 @@ function newPlottersManager () { function onProductPlottersInitialized (err) { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> Entering function.') } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> Received OK Response.') } - initializationReady = true callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE) break } case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> Received FAIL Response.') } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } default: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> Received Unexpected Response.') } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } @@ -179,8 +142,6 @@ function newPlottersManager () { function initializeCompetitionPlotters (callBack) { try { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> Entering function.') } - /* At this current version of the platform, we will support only one competition with only one plotter. */ const COMPETITION_HOST = 'AAArena' @@ -188,7 +149,6 @@ function newPlottersManager () { let objName = COMPETITION_HOST + '-' + COMPETITION let storage = newCompetitionStorage(objName) - let host = ecosystem.getHost(COMPETITION_HOST) let competition = ecosystem.getCompetition(host, COMPETITION) @@ -196,99 +156,70 @@ function newPlottersManager () { function onCompetitionStorageInitialized (err) { try { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> Entering function.') } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> Received OK Response.') } break } - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> Received FAIL Response.') } callBack(GLOBAL.DEFAULT_FAIL_RESPONSE) return } - default: { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> Received Unexpected Response.') } callBack(GLOBAL.DEFAULT_FAIL_RESPONSE) return } } - /* Now we have all the initial data loaded and ready to be delivered to the new instance of the plotter. */ - let plotter = getNewPlotter(competition.plotter.host, competition.plotter.codeName, competition.plotter.moduleName) - plotter.container.connectToParent(thisObject.container, true, true, false, true, true, true) - plotter.container.frame.position.x = thisObject.container.frame.width / 2 - plotter.container.frame.width / 2 plotter.container.frame.position.y = thisObject.container.frame.height / 2 - plotter.container.frame.height / 2 - /* We add the profile picture of each participant, because the plotter will need it. */ - for (let k = 0; k < competition.participants.length; k++) { let participant = competition.participants[k] let devTeam = ecosystem.getTeam(participant.devTeam) let bot = ecosystem.getBot(devTeam, participant.bot) participant.profilePicture = bot.profilePicture } - + plotter.fitFunction = thisObject.fitFunction plotter.initialize(competition, storage, datetime, timePeriod, onPlotterInizialized) function onPlotterInizialized (err) { try { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> Entering function.') } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> Received OK Response.') } break } - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> Received FAIL Response.') } callBack(GLOBAL.DEFAULT_FAIL_RESPONSE) return } - default: { - if (INFO_LOG === true) { logger.write('[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> Received Unexpected Response.') } callBack(GLOBAL.DEFAULT_FAIL_RESPONSE) return } } - let competitionPlotter = { plotter: plotter, storage: storage } - /* Add the new Active Protter to the Array */ - competitionPlotters.push(competitionPlotter) - /* Create The Profie Picture FloatingObject */ - if (competitionPlotter.plotter.payload !== undefined) { for (let k = 0; k < competition.participants.length; k++) { let participant = competition.participants[k] let devTeam = ecosystem.getTeam(participant.devTeam) let bot = ecosystem.getBot(devTeam, participant.bot) let imageId = participant.devTeam + '.' + participant.profilePicture - const TEAM = devTeam.codeName.toLowerCase() const BOT = bot.codeName.toLowerCase() let botAvatar = new Image() - botAvatar.src = window.canvasApp.context.fbProfileImages.get(TEAM + '-' + BOT) - competitionPlotter.plotter.payload[k].profile.title = bot.displayName competitionPlotter.plotter.payload[k].profile.imageId = imageId competitionPlotter.plotter.payload[k].profile.botAvatar = botAvatar - canvas.floatingSpace.profileBalls.createNewProfileBall(competitionPlotter.plotter.payload[k], onProfileBallCreated) function onProfileBallCreated (err, pProfileHandle) { @@ -296,7 +227,6 @@ function newPlottersManager () { } } } - callBack(GLOBAL.DEFAULT_OK_RESPONSE) } catch (err) { if (ERROR_LOG === true) { logger.write('[ERROR] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> err = ' + err.stack) } @@ -316,8 +246,6 @@ function newPlottersManager () { function initializeProductPlotters (callBack) { try { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> Entering function.') } - /* Lets get all the cards that needs to be loaded. */ let initializationCounter = 0 @@ -329,49 +257,30 @@ function newPlottersManager () { callBack(GLOBAL.DEFAULT_OK_RESPONSE) return } - for (let i = 0; i < loadingProductCards.length; i++) { /* For each one, we will initialize the associated plotter. */ - initializeProductPlotter(loadingProductCards[i], onProductPlotterInitialized) function onProductPlotterInitialized (err) { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> Entering function.') } - initializationCounter++ switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> Received OK Response.') } okCounter++ break } - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> Received FAIL Response.') } failCounter++ break } - default: { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> Received Unexpected Response.') } failCounter++ break } } - - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> initializationCounter = ' + initializationCounter) } - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> okCounter = ' + okCounter) } - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> failCounter = ' + failCounter) } - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> loadingProductCards.length = ' + loadingProductCards.length) } - if (initializationCounter === loadingProductCards.length) { - // This was the last one. - - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> Last Product Loaded.') } - - /* If less than 50% of plotters are initialized then we return FAIL. */ - + // This was the last one. + /* If less than 50% of plotters are initialized then we return FAIL. */ if (okCounter >= 1) { callBack(GLOBAL.DEFAULT_OK_RESPONSE) } else { @@ -388,22 +297,15 @@ function newPlottersManager () { function initializeProductPlotter (pProductCard, callBack) { try { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> Entering function.') } - /* Before Initializing a Plotter, we need the Storage it will use, loaded with the files it will initially need. */ - let objName = pProductCard.devTeam.codeName + '-' + pProductCard.bot.codeName + '-' + pProductCard.product.codeName let storage = newProductStorage(objName) - - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> objName = ' + objName) } - /* Before Initializing the Storage, we will put the Product Card to listen to the events the storage will raise every time a file is loaded, so that the UI can somehow show this. There are different types of events. */ - for (let i = 0; i < pProductCard.product.dataSets.length; i++) { let thisSet = pProductCard.product.dataSets[i] @@ -412,17 +314,14 @@ function newPlottersManager () { storage.eventHandler.listenToEvent('Market File Loaded', pProductCard.onMarketFileLoaded) } break - case 'Daily Files': { storage.eventHandler.listenToEvent('Daily File Loaded', pProductCard.onDailyFileLoaded) } break - case 'Single File': { storage.eventHandler.listenToEvent('Single File Loaded', pProductCard.onSingleFileLoaded) } break - case 'File Sequence': { storage.eventHandler.listenToEvent('File Sequence Loaded', pProductCard.onFileSequenceLoaded) } @@ -434,53 +333,30 @@ function newPlottersManager () { function onProductStorageInitialized (err) { try { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> Entering function.') } - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> key = ' + pProductCard.devTeam.codeName + '-' + pProductCard.bot.codeName + '-' + pProductCard.product.codeName) } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> Received OK Response.') } - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> The plotter will be started.') } break } - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> Received FAIL Response.') } - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> The plotter will not be started.') } callBack(GLOBAL.DEFAULT_FAIL_RESPONSE) return } - default: { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> Received Unexpected Response.') } - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> The plotter will not be started.') } callBack(GLOBAL.DEFAULT_FAIL_RESPONSE) return } } - /* Now we have all the initial data loaded and ready to be delivered to the new instance of the plotter. */ - let plotter = getNewPlotter(pProductCard.product.plotter.devTeam, pProductCard.product.plotter.codeName, pProductCard.product.plotter.moduleName) plotter.container.connectToParent(thisObject.container, true, true, false, true, true, true) - plotter.container.frame.position.x = thisObject.container.frame.width / 2 - plotter.container.frame.width / 2 plotter.container.frame.position.y = thisObject.container.frame.height / 2 - plotter.container.frame.height / 2 - + plotter.fitFunction = thisObject.fitFunction plotter.initialize(storage, exchange, market, datetime, timePeriod, onPlotterInizialized) function onPlotterInizialized () { try { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> onPlotterInizialized -> Entering function.') } - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotter -> onProductStorageInitialized -> onPlotterInizialized -> key = ' + pProductCard.product.plotter.devTeam + '-' + pProductCard.product.plotter.codeName + '-' + pProductCard.product.plotter.moduleName) } - - try { - // plotter.positionAtDatetime(NEW_SESSION_INITIAL_DATE); - } catch (err) { - // If the plotter does not implement this function its ok. - } - let productPlotter = { productCard: pProductCard, plotter: plotter, @@ -488,13 +364,9 @@ function newPlottersManager () { profile: undefined, notes: undefined } - /* Let the Plotter listen to the event of Cursor Files loaded, so that it can reack recalculating if needed. */ - storage.eventHandler.listenToEvent('Daily File Loaded', plotter.onDailyFileLoaded) - /* Lets load now this plotter panels. */ - productPlotter.panels = [] for (let i = 0; i < pProductCard.product.plotter.module.panels.length; i++) { @@ -506,53 +378,37 @@ function newPlottersManager () { moduleCodeName: pProductCard.product.plotter.moduleName, panelCodeName: panelConfig.codeName } - let panelOwner = exchange + ' ' + market.assetB + '/' + market.assetA let plotterPanelHandle = canvas.panelsSpace.createNewPanel('Plotter Panel', parameters, panelOwner) let plotterPanel = canvas.panelsSpace.getPanel(plotterPanelHandle, panelOwner) - /* Connect Panel to the Plotter via an Event. */ - if (panelConfig.event !== undefined) { productPlotter.plotter.container.eventHandler.listenToEvent(panelConfig.event, plotterPanel.onEventRaised) } - productPlotter.panels.push(plotterPanelHandle) } - /* Create The Profie Picture FloatingObject */ - if (productPlotter.plotter.payload !== undefined) { let imageId = pProductCard.bot.devTeam + '.' + pProductCard.bot.profilePicture - productPlotter.plotter.payload.profile.subTitle = pProductCard.product.shortDisplayName productPlotter.plotter.payload.profile.title = pProductCard.bot.displayName productPlotter.plotter.payload.profile.imageId = imageId productPlotter.plotter.payload.profile.botAvatar = pProductCard.bot.avatar - canvas.floatingSpace.profileBalls.createNewProfileBall(productPlotter.plotter.payload, onProfileBallCreated) function onProfileBallCreated (err, pProfileHandle) { productPlotter.profile = pProfileHandle - /* There is no policy yet of what to do if this fails. */ } - /* Create the Text Notes */ - canvas.floatingSpace.noteSets.createNoteSet(productPlotter.plotter.payload, productPlotter.plotter.container.eventHandler, onNoteSetCreated) - function onNoteSetCreated (err, pNoteSetHandle) { productPlotter.noteSet = pNoteSetHandle - /* There is no policy yet of what to do if this fails. */ } } - /* Add the new Active Protter to the Array */ - productPlotters.push(productPlotter) - callBack(GLOBAL.DEFAULT_OK_RESPONSE) } catch (err) { if (ERROR_LOG === true) { logger.write('[ERROR] initializeProductPlotter -> onProductStorageInitialized -> onPlotterInizialized -> err = ' + err.stack) } @@ -571,60 +427,41 @@ function newPlottersManager () { } function onProductCardStatusChanged (pProductCard) { - if (INFO_LOG === true) { logger.write('[INFO] onProductCardStatusChanged -> Entering function.') } - if (pProductCard.status === PRODUCT_CARD_STATUS.LOADING) { /* Lets see if we can find the Plotter of this card on our Active Plotters list, other wise we will initialize it */ - let found = false - for (let i = 0; i < productPlotters.length; i++) { if (productPlotters[i].productCard.code === pProductCard.code) { found = true } } - if (found === false) { initializeProductPlotter(pProductCard, onProductPlotterInitialized) - function onProductPlotterInitialized (err) { - if (INFO_LOG === true) { logger.write('[INFO] initializeProductPlotters -> onProductPlotterInitialized -> Entering function.') } - /* There is no policy yet of what to do if this fails. */ } } } - if (pProductCard.status === PRODUCT_CARD_STATUS.OFF) { /* If the plotter of this card is not on our Active Plotters list, then we remove it. */ - for (let i = 0; i < productPlotters.length; i++) { if (productPlotters[i].productCard.code === pProductCard.code) { if (productPlotters[i].profile !== undefined) { canvas.floatingSpace.profileBalls.destroyProfileBall(productPlotters[i].profile) } - /* Destroyd the Note Set */ - canvas.floatingSpace.noteSets.destroyNoteSet(productPlotters[i].noteSet) - /* Then the panels. */ - for (let j = 0; j < productPlotters[i].panels.length; j++) { canvas.panelsSpace.destroyPanel(productPlotters[i].panels[j]) } - /* Finally the Storage Objects */ - productPlotters[i].storage.finalize() - if (productPlotters[i].plotter.finalize !== undefined) { productPlotters[i].plotter.container.finalize() productPlotters[i].plotter.finalize() } - productPlotters.splice(i, 1) // Delete item from array. - return // We already found the product woth changes and processed it. } } @@ -632,53 +469,38 @@ function newPlottersManager () { } function setTimePeriod (pTimePeriod) { - if (INFO_LOG === true) { logger.write('[INFO] setTimePeriod -> Entering function.') } - timePeriod = pTimePeriod - if (initializationReady === true) { for (let i = 0; i < productPlotters.length; i++) { let productPlotter = productPlotters[i] - productPlotter.productCard.setTimePeriod(timePeriod) productPlotter.storage.setTimePeriod(timePeriod) productPlotter.plotter.setTimePeriod(timePeriod) } - for (let i = 0; i < competitionPlotters.length; i++) { let competitionPlotter = competitionPlotters[i] - competitionPlotter.plotter.setTimePeriod(timePeriod) } } } function setDatetime (pDatetime) { - if (INFO_LOG === true) { logger.write('[INFO] setDatetime -> Entering function.') } - datetime = pDatetime - for (let i = 0; i < productPlotters.length; i++) { let productPlotter = productPlotters[i] - productPlotter.productCard.setDatetime(pDatetime) productPlotter.storage.setDatetime(pDatetime) productPlotter.plotter.setDatetime(pDatetime) - } - - for (let i = 0; i < competitionPlotters.length; i++) { - let competitionPlotter = competitionPlotters[i] - - competitionPlotter.plotter.setDatetime(datetime) + for (let i = 0; i < competitionPlotters.length; i++) { + let competitionPlotter = competitionPlotters[i] + competitionPlotter.plotter.setDatetime(datetime) + } } } function positionAtDatetime (pDatetime) { - if (INFO_LOG === true) { logger.write('[INFO] positionAtDatetime -> Entering function.') } - for (let i = 0; i < productPlotters.length; i++) { let productPlotter = productPlotters[i] - if (productPlotter.plotter.positionAtDatetime !== undefined) { productPlotter.plotter.positionAtDatetime(pDatetime) } @@ -686,30 +508,20 @@ function newPlottersManager () { } function getContainer (point) { - if (INFO_LOG === true) { logger.write('[INFO] getContainer -> Entering function.') } - - let container - - return container } function draw () { - if (INTENSIVE_LOG === true) { logger.write('[INFO] draw -> Entering function.') } - if (productPlotters === undefined) { return } // We need to wait - /* First the Product Plotters. */ - for (let i = 0; i < productPlotters.length; i++) { let productPlotter = productPlotters[i] productPlotter.plotter.draw() } - /* Then the competition plotters. */ - for (let i = 0; i < competitionPlotters.length; i++) { let competitionPlotter = competitionPlotters[i] competitionPlotter.plotter.draw() } } } + diff --git a/ChartsSpace/TimeMachine.js b/ChartsSpace/TimeMachine.js index 4ef15162d..0f01ddf25 100644 --- a/ChartsSpace/TimeMachine.js +++ b/ChartsSpace/TimeMachine.js @@ -18,6 +18,7 @@ function newTimeMachine () { container: undefined, timeScale: undefined, rateScale: undefined, + fitFunction: undefined, charts: [], physics: physics, drawBackground: drawBackground, @@ -65,6 +66,7 @@ function newTimeMachine () { thisObject.timeScale = undefined thisObject.rateScale.finalize() thisObject.rateScale = undefined + thisObject.fitFunction = undefined } function initialize (callBackFunction) { @@ -106,6 +108,7 @@ function newTimeMachine () { /* Each Time Machine has a Time Scale and a Right Scale. */ thisObject.timeScale = newTimeScale() + thisObject.timeScale.fitFunction = thisObject.fitFunction thisObject.timeScale.container.eventHandler.listenToEvent('Lenght Percentage Changed', function (event) { thisObject.container.frame.width = TIME_MACHINE_WIDTH * event.lenghtPercentage / 100 @@ -117,6 +120,7 @@ function newTimeMachine () { thisObject.timeScale.initialize() thisObject.rateScale = newRateScale() + thisObject.rateScale.fitFunction = thisObject.fitFunction thisObject.rateScale.container.eventHandler.listenToEvent('Height Percentage Changed', function (event) { thisObject.container.frame.height = TIME_MACHINE_HEIGHT * event.heightPercentage / 100 diff --git a/ChartsSpace/TimelineChart.js b/ChartsSpace/TimelineChart.js index bce46cf18..82a09b62c 100644 --- a/ChartsSpace/TimelineChart.js +++ b/ChartsSpace/TimelineChart.js @@ -132,6 +132,7 @@ function newTimelineChart () { plotterManager.container.frame.position.x = 0 plotterManager.container.frame.position.y = 0 + plotterManager.fitFunction = thisObject.fitFunction plotterManager.initialize(productsPanel, pExchange, pMarket, onPlotterManagerReady) function onPlotterManagerReady (err) { diff --git a/CockpitSpace/FullScreen.js b/CockpitSpace/FullScreen.js new file mode 100644 index 000000000..2218de0c0 --- /dev/null +++ b/CockpitSpace/FullScreen.js @@ -0,0 +1,167 @@ + +function newFullScreen () { + const MODULE_NAME = 'Fullscreen' + + let thisObject = { + visible: true, + container: undefined, + status: undefined, + physics: physics, + draw: draw, + getContainer: getContainer, + initialize: initialize, + finalize: finalize + } + + thisObject.container = newContainer() + thisObject.container.initialize(MODULE_NAME) + + thisObject.container.isDraggeable = false + thisObject.container.isWheelable = false + thisObject.container.isClickeable = true + thisObject.container.detectMouseOver = true + + let selfMouseOverEventSubscriptionId + let selfMouseClickEventSubscriptionId + let selfMouseNotOverEventSubscriptionId + + let isMouseOver = false + let counterTillNextState = 0 + + let productCardsToTurnOn = [] + return thisObject + + function finalize () { + thisObject.container.eventHandler.stopListening(selfMouseOverEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfMouseNotOverEventSubscriptionId) + + thisObject.container.finalize() + thisObject.container = undefined + } + + function initialize () { + thisObject.container.frame.width = 50 + thisObject.container.frame.height = COCKPIT_SPACE_HEIGHT - 12 + + selfMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) + selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) + selfMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver) + + thisObject.status = 'Ready' + } + + function getContainer (point, purpose) { + if (thisObject.visible !== true) { return } + + if (thisObject.container.frame.isThisPointHere(point, true) === true) { + return thisObject.container + } else { + return undefined + } + } + + function onMouseOver (point) { + if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { + isMouseOver = true + } else { + isMouseOver = false + } + } + + function onMouseNotOver (point) { + isMouseOver = false + } + + function onMouseClick (event) { + if (thisObject.status !== 'On') { + thisObject.status = 'On' + goFullscreen() + CURRENT_TOP_MARGIN = 0 + } else { + thisObject.status = 'Off' + goOutFullscreen() + CURRENT_TOP_MARGIN = window.canvasApp.topMargin + } + } + + function goFullscreen () { + if (browserCanvas.requestFullscreen) { + browserCanvas.requestFullscreen() + } else if (browserCanvas.mozRequestFullScreen) { + browserCanvas.mozRequestFullScreen() + } else if (browserCanvas.webkitRequestFullscreen) { + browserCanvas.webkitRequestFullscreen() + } else if (browserCanvas.msRequestFullscreen) { browserCanvas.msRequestFullscreen() } + } + + function goOutFullscreen () { + if (document.exitFullscreen) { + document.exitFullscreen() + } else if (document.mozCancelFullScreen) { + document.mozCancelFullScreen() + } else if (document.webkitExitFullscreen) { + document.webkitExitFullscreen() + } else if (document.msExitFullscreen) { document.msExitFullscreen() } + } + + function physics () { + positionPhysics() + } + + function positionPhysics () { + thisObject.container.frame.position.x = thisObject.container.parentContainer.frame.width - thisObject.container.frame.width + thisObject.container.frame.position.y = 6 + } + + function draw () { + if (thisObject.visible !== true) { return } + drawBackground() + drawText() + } + + function drawBackground () { + let params = { + cornerRadius: 3, + lineWidth: 0.01, + container: thisObject.container, + borderColor: UI_COLOR.DARK, + castShadow: false, + opacity: 1 + } + + if (isMouseOver === true) { + params.backgroundColor = UI_COLOR.TURQUOISE + } else { + params.backgroundColor = UI_COLOR.DARK_TURQUOISE + } + + roundedCornersBackground(params) + } + + function drawText () { + let fontSize + let label + let xOffset + let yOffset + + const OPACITY = 1 + + /* We put the params.VALUE in the middle */ + + fontSize = 15 + + browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY + label = 'FS' + + let labelPoint = { + x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO, + y: thisObject.container.frame.height - 9 + } + labelPoint = thisObject.container.frame.frameThisPoint(labelPoint) + + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')' + browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) + } +} + diff --git a/CockpitSpace/RestartSimulation.js b/CockpitSpace/RestartSimulation.js index f82de480c..97a494a7a 100644 --- a/CockpitSpace/RestartSimulation.js +++ b/CockpitSpace/RestartSimulation.js @@ -28,6 +28,7 @@ function newRestartSimulation () { let isMouseOver = false let counterTillNextState = 0 + let productCardsToTurnOn = [] return thisObject function finalize () { @@ -40,8 +41,6 @@ function newRestartSimulation () { } function initialize () { - thisObject.container.frame.position.x = thisObject.container.parentContainer.frame.width * 80 / 100 - thisObject.container.frame.position.y = 6 thisObject.container.frame.width = 250 thisObject.container.frame.height = COCKPIT_SPACE_HEIGHT - 12 @@ -103,16 +102,46 @@ function newRestartSimulation () { timePeriodMarketArray: timePeriodMarketArray } try { - thisObject.status = 'Restarting' - await graphQlRestartSimulation(simulationParams) - thisObject.status = 'Calculating' - counterTillNextState = 1000 + thisObject.status = 'Saving' + let result = await canvas.strategySpace.strategizerGateway.saveToStrategyzer(canvas.strategySpace.workspace.tradingSystem) + if (result === true) { + thisObject.status = 'Restarting' + await graphQlRestartSimulation(simulationParams) + thisObject.status = 'Calculating' + counterTillNextState = 2000 + } else { + thisObject.status = 'Error' + counterTillNextState = 500 + } } catch (err) { thisObject.status = 'Error' counterTillNextState = 500 } } + function turnOffProductCards () { + for (let i = 0; i < canvas.panelsSpace.panels.length; i++) { + let panel = canvas.panelsSpace.panels[i] + if (panel.name === 'Products Panel') { + for (j = 0; j < panel.productCards.length; j++) { + let productCard = panel.productCards[j] + if (productCard.product.relatedToTradingEngine === true && productCard.status !== PRODUCT_CARD_STATUS.OFF) { + productCard.turnOff() + productCardsToTurnOn.push(productCard) + } + } + } + } + } + + function turnOnProductCards () { + for (let i = 0; i < productCardsToTurnOn.length; i++) { + let productCard = productCardsToTurnOn[i] + productCard.turnOn() + } + productCardsToTurnOn = [] + } + function physics () { if (counterTillNextState > 0) { counterTillNextState-- @@ -121,12 +150,21 @@ function newRestartSimulation () { switch (thisObject.status) { case 'Ready': + break + case 'Saving': + break case 'Restarting': break case 'Calculating': + thisObject.status = 'Refreshing' + turnOffProductCards() + counterTillNextState = 250 + break + case 'Refreshing': thisObject.status = 'Ready' + turnOnProductCards() break case 'Error': thisObject.status = 'Ready' @@ -134,6 +172,13 @@ function newRestartSimulation () { } } } + + positionPhysics() + } + + function positionPhysics () { + thisObject.container.frame.position.x = thisObject.container.parentContainer.frame.width - thisObject.container.frame.width - 50 + thisObject.container.frame.position.y = 6 } function draw () { @@ -161,9 +206,15 @@ function newRestartSimulation () { } break } + case 'Saving': + params.backgroundColor = UI_COLOR.MANGANESE_PURPLE + break case 'Restarting': params.backgroundColor = UI_COLOR.GREY break + case 'Refreshing': + params.backgroundColor = UI_COLOR.GOLDEN_ORANGE + break case 'Calculating': params.backgroundColor = UI_COLOR.TITANIUM_YELLOW break @@ -191,21 +242,27 @@ function newRestartSimulation () { switch (thisObject.status) { case 'Ready': - label = 'RESTART SIMULATION' + label = 'RE-CALCULATE' + break + case 'Saving': + label = 'SAVING STRATEGIES CHANGES...' break case 'Restarting': - label = 'RESTARTING...' + label = 'RESTARTING TRADING ENGINE...' + break + case 'Refreshing': + label = 'RE-LOADING...' break case 'Calculating': label = 'CALCULATING...' break case 'Error': - label = 'NOT POSSIBLE NOW' + label = 'ERROR, RETRY LATER' break } let labelPoint = { - x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO - 15, + x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO - 25, y: thisObject.container.frame.height - 9 } labelPoint = thisObject.container.frame.frameThisPoint(labelPoint) diff --git a/Container.js b/Container.js index 2b467470f..2753e96d9 100644 --- a/Container.js +++ b/Container.js @@ -33,6 +33,8 @@ function newContainer () { let onFocusParentEvent = false let onNotFocusParentEvent = false let onDisplaceParentEvent = false + let onDragStartedParentEvent = false + let onDragFinishedParentEvent = false let isConnectedToParent = false @@ -42,6 +44,8 @@ function newContainer () { let onFocusEventSubscriptionId let onNotFocusEventSubscriptionId let onDisplaceEventSubscriptionId + let onDragStartedEventSubscriptionId + let onDragFinishedEventSubscriptionId return thisObject @@ -53,6 +57,8 @@ function newContainer () { thisObject.parentContainer.eventHandler.stopListening(onFocusEventSubscriptionId) thisObject.parentContainer.eventHandler.stopListening(onNotFocusEventSubscriptionId) thisObject.parentContainer.eventHandler.stopListening(onDisplaceEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onDragStartedEventSubscriptionId) + thisObject.parentContainer.eventHandler.stopListening(onDragFinishedEventSubscriptionId) thisObject.parentContainer = undefined thisObject.eventHandler.finalize() @@ -86,7 +92,9 @@ function newContainer () { onMouseNotOverEvent, onFocusEvent, onNotFocusEvent, - onDisplaceEvent + onDisplaceEvent, + onDragStartedEvent, + onDragFinishedEvent ) { connectedToParentWidth = onWidth connectedToParentHeight = onHeight @@ -98,6 +106,8 @@ function newContainer () { onFocusParentEvent = onFocusEvent onNotFocusParentEvent = onNotFocusEvent onDisplaceParentEvent = onDisplaceEvent + onDragStartedParentEvent = onDragStartedEvent + onDragFinishedParentEvent = onDragFinishedEvent isConnectedToParent = true @@ -123,6 +133,12 @@ function newContainer () { if (onDisplaceParentEvent === true) { onDisplaceEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDisplace', onDisplace) } + if (onDragStartedParentEvent === true) { + onDragStartedEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDragStarted', onDragStarted) + } + if (onDragFinishedParentEvent === true) { + onDragFinishedEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onDragFinished', onDragFinished) + } if (connectedToParentWidth) { thisObject.frame.width = thisObject.parentContainer.frame.width @@ -175,6 +191,14 @@ function newContainer () { thisObject.eventHandler.raiseEvent('onDisplace', event) } + function onDragStarted (event) { + thisObject.eventHandler.raiseEvent('onDragStarted', event) + } + + function onDragFinished (event) { + thisObject.eventHandler.raiseEvent('onDragFinished', event) + } + function isForThisPurpose (purpose) { if (purpose !== undefined) { switch (purpose) { diff --git a/Dashboard.js b/Dashboard.js index 63e3176af..663c7cfa6 100644 --- a/Dashboard.js +++ b/Dashboard.js @@ -80,7 +80,7 @@ function newDashboard () { browserCanvas = document.getElementById('canvas') browserCanvas.width = window.innerWidth - browserCanvas.height = window.innerHeight - window.canvasApp.topMargin + browserCanvas.height = window.innerHeight - CURRENT_TOP_MARGIN viewPort.initialize() } catch (err) { diff --git a/FloatingSpace/CircularMenu.js b/FloatingSpace/CircularMenu.js index d79c9c099..89113c067 100644 --- a/FloatingSpace/CircularMenu.js +++ b/FloatingSpace/CircularMenu.js @@ -65,6 +65,7 @@ function newCircularMenu () { menuItem.angle = menuItemInitialValue.angle menuItem.currentStatus = menuItemInitialValue.currentStatus menuItem.relatedStrategyPart = menuItemInitialValue.relatedStrategyPart + menuItem.dontShowAtFullscreen = menuItemInitialValue.dontShowAtFullscreen if (menuItem.label === undefined) { menuItem.type = 'Icon Only' diff --git a/FloatingSpace/CircularMenuItem.js b/FloatingSpace/CircularMenuItem.js index 30553ac7a..b4f191322 100644 --- a/FloatingSpace/CircularMenuItem.js +++ b/FloatingSpace/CircularMenuItem.js @@ -24,6 +24,7 @@ function newCircularMenuItem () { container: undefined, payload: undefined, relatedStrategyPart: undefined, + dontShowAtFullscreen: undefined, physics: physics, drawBackground: drawBackground, drawForeground: drawForeground, @@ -88,6 +89,8 @@ function newCircularMenuItem () { } function getContainer (point) { + if (thisObject.dontShowAtFullscreen === true && CURRENT_TOP_MARGIN === 0) { return } + let container if (thisObject.isDeployed === true) { if (thisObject.container.frame.isThisPointHere(point, true, false) === true) { @@ -99,7 +102,9 @@ function newCircularMenuItem () { } function physics () { - const INCREASE_STEP = 0.25 + if (thisObject.dontShowAtFullscreen === true && CURRENT_TOP_MARGIN === 0) { return } + + const INCREASE_STEP = 1 if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= INCREASE_STEP) { if (thisObject.currentRadius < thisObject.targetRadius) { @@ -183,6 +188,8 @@ function newCircularMenuItem () { } function drawBackground () { + if (thisObject.dontShowAtFullscreen === true && CURRENT_TOP_MARGIN === 0) { return } + if (thisObject.container.frame.position.x > 0 && thisObject.isDeployed === true && thisObject.currentRadius >= thisObject.targetRadius) { if (thisObject.type === 'Icon & Text') { let params = { @@ -206,6 +213,8 @@ function newCircularMenuItem () { } function drawForeground () { + if (thisObject.dontShowAtFullscreen === true && CURRENT_TOP_MARGIN === 0) { return } + let menuPosition = { x: thisObject.currentRadius * 1.5, y: thisObject.container.frame.height / 2 @@ -247,3 +256,4 @@ function newCircularMenuItem () { } } } + diff --git a/FloatingSpace/CodeEditor.js b/FloatingSpace/CodeEditor.js index 0d3b28d1d..8df2e4311 100644 --- a/FloatingSpace/CodeEditor.js +++ b/FloatingSpace/CodeEditor.js @@ -140,7 +140,7 @@ function newCodeEditor () { if (thisObject.visible === true) { let checkPosition = { x: textAreaPosition.x, - y: textAreaPosition.y - window.canvasApp.topMargin + y: textAreaPosition.y - CURRENT_TOP_MARGIN } if (thisObject.isVisibleFunction(checkPosition) === false) { deactivate() diff --git a/FloatingSpace/FloatingLayer.js b/FloatingSpace/FloatingLayer.js index f3889b6f1..a366a4b40 100644 --- a/FloatingSpace/FloatingLayer.js +++ b/FloatingSpace/FloatingLayer.js @@ -15,7 +15,6 @@ function newFloatingLayer () { const MODULE_NAME = 'Floating Layer' - const INFO_LOG = false const ERROR_LOG = true const logger = newWebDebugLog() logger.fileName = MODULE_NAME @@ -121,8 +120,6 @@ function newFloatingLayer () { */ try { - if (INFO_LOG === true) { logger.write('[INFO] getFloatingObject -> Entering function.') } - if (pFloatingObjectHandle !== undefined) { for (let i = 0; i < invisibleFloatingObjects.length; i++) { let floatingObject = invisibleFloatingObjects[i] @@ -397,6 +394,8 @@ function newFloatingLayer () { targetRepulsionForce(i) gravityForce(floatingObject, payload) + + proximityBetweenFloatingObjects(i) } } catch (err) { if (ERROR_LOG === true) { logger.write('[ERROR] physics -> applyPhysics -> err= ' + err.stack) } @@ -436,36 +435,36 @@ function newFloatingLayer () { const coulomb = 0.00001 const minForce = 0.01 - var d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject.container.frame.position.y, 2)) // ... we calculate the distance ... + let d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject.container.frame.position.y, 2)) // ... we calculate the distance ... - var force = coulomb * d * d / floatingObject.currentMass // In this case the mass of the floatingObject affects the gravity force that it receives, that gives priority to target position to bigger floatingObjects. + let force = coulomb * d * d / floatingObject.currentMass // In this case the mass of the floatingObject affects the gravity force that it receives, that gives priority to target position to bigger floatingObjects. if (force < minForce) { // We need this attraction force to overcome the friction we imposed to the system. force = minForce } - var pos1 = { + let pos1 = { x: floatingObject.container.frame.position.x, y: floatingObject.container.frame.position.y } - var pos2 = { + let pos2 = { x: payload.targetPosition.x, y: payload.targetPosition.y } - var posDiff = { // Next we need the vector resulting from the 2 positions. + let posDiff = { // Next we need the vector resulting from the 2 positions. x: pos2.x - pos1.x, y: pos2.y - pos1.y } if (d !== 0) { - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. x: posDiff.x / d, y: posDiff.y / d } - var forceVector = { + let forceVector = { x: unitVector.x * force, y: unitVector.y * force } @@ -486,19 +485,19 @@ function newFloatingLayer () { const coulomb = 2 - var floatingObject1 = visibleFloatingObjects[currentFloatingObject] + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] - for (var i = 0; i < visibleFloatingObjects.length; i++) { + for (let i = 0; i < visibleFloatingObjects.length; i++) { // The force to be applied is considering all other floatingObjects... if (i !== currentFloatingObject) { // ... except for the current one. - var floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... - var d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... - var force = coulomb * floatingObject2.currentMass / (d * d) // ... and with it the repulsion force. + let force = coulomb * floatingObject2.currentMass / (d * d) // ... and with it the repulsion force. /* We need to put a hard limit to this force, in order to to eject very little floatingObjects to the infinite and beyond. */ @@ -506,27 +505,27 @@ function newFloatingLayer () { force = 1 } - var pos1 = { + let pos1 = { x: floatingObject1.container.frame.position.x, y: floatingObject1.container.frame.position.y } - var pos2 = { + let pos2 = { x: floatingObject2.container.frame.position.x, y: floatingObject2.container.frame.position.y } - var posDiff = { // Next we need the vector resulting from the 2 positions. + let posDiff = { // Next we need the vector resulting from the 2 positions. x: pos2.x - pos1.x, y: pos2.y - pos1.y } - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. x: posDiff.x / d, y: posDiff.y / d } - var forceVector = { + let forceVector = { x: unitVector.x * force, y: unitVector.y * force } @@ -542,18 +541,52 @@ function newFloatingLayer () { } } + function proximityBetweenFloatingObjects (currentFloatingObject) { + try { + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] + floatingObject1.nearbyFloatingObjects = [] + for (let i = 0; i < visibleFloatingObjects.length; i++) { + if (i !== currentFloatingObject) { + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + + if (floatingObject1.nearbyFloatingObjects.length === 0) { + floatingObject1.nearbyFloatingObjects.push([d, floatingObject2]) + } else { + let recordAdded = false + for (let j = 0; j < floatingObject1.nearbyFloatingObjects.length; j++) { + let recordedDistance = floatingObject1.nearbyFloatingObjects[j][0] + if (d < recordedDistance) { + floatingObject1.nearbyFloatingObjects.splice(j, 0, [d, floatingObject2]) + recordAdded = true + break + } + } + if (recordAdded === false) { + floatingObject1.nearbyFloatingObjects.push([d, floatingObject2]) + } + } + floatingObject1.nearbyFloatingObjects.splice(10, floatingObject1.nearbyFloatingObjects.length) + } + } + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] proximityBetweenFloatingObjects -> err= ' + err.stack) } + } + } + function targetRepulsionForce (currentFloatingObject) { try { /* We generate a repulsion force between floatingObjects, that prevents them to be collisioning so often. */ const coulomb = 2 - var floatingObject1 = visibleFloatingObjects[currentFloatingObject] + let floatingObject1 = visibleFloatingObjects[currentFloatingObject] - for (var i = 0; i < visibleFloatingObjects.length; i++) { + for (let i = 0; i < visibleFloatingObjects.length; i++) { // The force to be applied is considering all other floatingObjects... - var floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... + let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject... let payload = { position: undefined @@ -578,9 +611,9 @@ function newFloatingLayer () { } } - var d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject1.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... + let d = Math.sqrt(Math.pow(payload.targetPosition.x - floatingObject1.container.frame.position.x, 2) + Math.pow(payload.targetPosition.y - floatingObject1.container.frame.position.y, 2)) // ... we calculate the distance ... - var force = coulomb * floatingObject2.currentMass / (d * d) // ... and with it the repulsion force. + let force = coulomb * floatingObject2.currentMass / (d * d) // ... and with it the repulsion force. /* We need to put a hard limit to this force, in order to to eject very little floatingObjects to the infinite and beyond. */ @@ -588,28 +621,28 @@ function newFloatingLayer () { force = maxTargetRepulsionForce } - var pos1 = { + let pos1 = { x: floatingObject1.container.frame.position.x, y: floatingObject1.container.frame.position.y } - var pos2 = { + let pos2 = { x: payload.targetPosition.x, y: payload.targetPosition.y } - var posDiff = { // Next we need the vector resulting from the 2 positions. + let posDiff = { // Next we need the vector resulting from the 2 positions. x: pos2.x - pos1.x, y: pos2.y - pos1.y } if (d !== 0) { - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. + let unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. x: posDiff.x / d, y: posDiff.y / d } - var forceVector = { + let forceVector = { x: unitVector.x * force, y: unitVector.y * force } @@ -647,10 +680,10 @@ function newFloatingLayer () { try { /* This function detects weather 2 floatingObjects collide with each other. */ - var r1 = floatingObject1.container.frame.radius - var r2 = floatingObject2.container.frame.radius + let r1 = floatingObject1.container.frame.radius + let r2 = floatingObject2.container.frame.radius - var distance = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) + let distance = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) if (distance > (r1 + r2)) { // No solutions, the circles are too far apart. @@ -675,26 +708,26 @@ function newFloatingLayer () { try { /* This function changes speed and position of floatingObjects that are in collision */ - var collisionision_angle = Math.atan2((floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y), (floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x)) + let collisionision_angle = Math.atan2((floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y), (floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x)) - var speed1 = Math.sqrt(floatingObject1.currentSpeed.x * floatingObject1.currentSpeed.x + floatingObject1.currentSpeed.y * floatingObject1.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 1 - var speed2 = Math.sqrt(floatingObject2.currentSpeed.x * floatingObject2.currentSpeed.x + floatingObject2.currentSpeed.y * floatingObject2.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 2 + let speed1 = Math.sqrt(floatingObject1.currentSpeed.x * floatingObject1.currentSpeed.x + floatingObject1.currentSpeed.y * floatingObject1.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 1 + let speed2 = Math.sqrt(floatingObject2.currentSpeed.x * floatingObject2.currentSpeed.x + floatingObject2.currentSpeed.y * floatingObject2.currentSpeed.y) // Magnitude of Speed Vector for floatingObject 2 - var direction_1 = Math.atan2(floatingObject1.currentSpeed.y, floatingObject1.currentSpeed.x) - var direction_2 = Math.atan2(floatingObject2.currentSpeed.y, floatingObject2.currentSpeed.x) + let direction_1 = Math.atan2(floatingObject1.currentSpeed.y, floatingObject1.currentSpeed.x) + let direction_2 = Math.atan2(floatingObject2.currentSpeed.y, floatingObject2.currentSpeed.x) - var new_xspeed_1 = speed1 * Math.cos(direction_1 - collisionision_angle) - var new_yspeed_1 = speed1 * Math.sin(direction_1 - collisionision_angle) - var new_xspeed_2 = speed2 * Math.cos(direction_2 - collisionision_angle) - var new_yspeed_2 = speed2 * Math.sin(direction_2 - collisionision_angle) + let new_xspeed_1 = speed1 * Math.cos(direction_1 - collisionision_angle) + let new_yspeed_1 = speed1 * Math.sin(direction_1 - collisionision_angle) + let new_xspeed_2 = speed2 * Math.cos(direction_2 - collisionision_angle) + let new_yspeed_2 = speed2 * Math.sin(direction_2 - collisionision_angle) - var final_xspeed_1 = ((floatingObject1.currentMass - floatingObject2.currentMass) * new_xspeed_1 + (floatingObject2.currentMass + floatingObject2.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) - var final_xspeed_2 = ((floatingObject1.currentMass + floatingObject1.currentMass) * new_xspeed_1 + (floatingObject2.currentMass - floatingObject1.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) - var final_yspeed_1 = new_yspeed_1 - var final_yspeed_2 = new_yspeed_2 + let final_xspeed_1 = ((floatingObject1.currentMass - floatingObject2.currentMass) * new_xspeed_1 + (floatingObject2.currentMass + floatingObject2.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) + let final_xspeed_2 = ((floatingObject1.currentMass + floatingObject1.currentMass) * new_xspeed_1 + (floatingObject2.currentMass - floatingObject1.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass) + let final_yspeed_1 = new_yspeed_1 + let final_yspeed_2 = new_yspeed_2 - var cosAngle = Math.cos(collisionision_angle) - var sinAngle = Math.sin(collisionision_angle) + let cosAngle = Math.cos(collisionision_angle) + let sinAngle = Math.sin(collisionision_angle) floatingObject1.currentSpeed.x = cosAngle * final_xspeed_1 - sinAngle * final_yspeed_1 floatingObject1.currentSpeed.y = sinAngle * final_xspeed_1 + cosAngle * final_yspeed_1 @@ -702,36 +735,36 @@ function newFloatingLayer () { floatingObject2.currentSpeed.x = cosAngle * final_xspeed_2 - sinAngle * final_yspeed_2 floatingObject2.currentSpeed.y = sinAngle * final_xspeed_2 + cosAngle * final_yspeed_2 - var pos1 = { + let pos1 = { x: floatingObject1.container.frame.position.x, y: floatingObject1.container.frame.position.y } - var pos2 = { + let pos2 = { x: floatingObject2.container.frame.position.x, y: floatingObject2.container.frame.position.y } // get the mtd - var posDiff = { + let posDiff = { x: pos1.x - pos2.x, y: pos1.y - pos2.y } - var d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) + let d = Math.sqrt(Math.pow(floatingObject2.container.frame.position.x - floatingObject1.container.frame.position.x, 2) + Math.pow(floatingObject2.container.frame.position.y - floatingObject1.container.frame.position.y, 2)) // minimum translation distance to push floatingObjects apart after intersecting - var scalar = (((floatingObject1.container.frame.radius + floatingObject2.container.frame.radius) - d) / d) + let scalar = (((floatingObject1.container.frame.radius + floatingObject2.container.frame.radius) - d) / d) - var minTD = { + let minTD = { x: posDiff.x * scalar, y: posDiff.y * scalar } // resolve intersection -- // computing inverse mass quantities - var im1 = 1 / floatingObject1.currentMass - var im2 = 1 / floatingObject2.currentMass + let im1 = 1 / floatingObject1.currentMass + let im2 = 1 / floatingObject2.currentMass // push-pull them apart based off their mass diff --git a/FloatingSpace/FloatingObject.js b/FloatingSpace/FloatingObject.js index 818d734da..266a7cd0c 100644 --- a/FloatingSpace/FloatingObject.js +++ b/FloatingSpace/FloatingObject.js @@ -20,6 +20,7 @@ function newFloatingObject () { rawRadius: 0, // This is the radius of this floating object without zoom. targetRadius: 0, // This is the target radius of the floating object with zoom applied. It should be animated until reaching this value. isPinned: false, + nearbyFloatingObjects: [], pinToggle: pinToggle, physics: physics, initializeMass: initializeMass, @@ -65,6 +66,7 @@ function newFloatingObject () { thisObject.container = undefined thisObject.payload = undefined thisObject.fitFunction = undefined + thisObject.nearbyFloatingObjects = undefined } function initialize (type, payload) { @@ -115,32 +117,32 @@ function newFloatingObject () { function thisObjectPhysics () { // The radius also have a target. - if (Math.abs(thisObject.container.frame.radius - thisObject.targetRadius) >= 3) { + if (Math.abs(thisObject.container.frame.radius - thisObject.targetRadius) >= 15) { if (thisObject.container.frame.radius < thisObject.targetRadius) { - thisObject.container.frame.radius = thisObject.container.frame.radius + 3 + thisObject.container.frame.radius = thisObject.container.frame.radius + 15 } else { - thisObject.container.frame.radius = thisObject.container.frame.radius - 3 + thisObject.container.frame.radius = thisObject.container.frame.radius - 15 } thisObject.container.eventHandler.raiseEvent('Dimmensions Changed', event) } // The imageSize also have a target. - if (Math.abs(thisObject.currentImageSize - thisObject.targetImageSize) >= 1) { + if (Math.abs(thisObject.currentImageSize - thisObject.targetImageSize) >= 10) { if (thisObject.currentImageSize < thisObject.targetImageSize) { - thisObject.currentImageSize = thisObject.currentImageSize + 2 + thisObject.currentImageSize = thisObject.currentImageSize + 10 } else { - thisObject.currentImageSize = thisObject.currentImageSize - 2 + thisObject.currentImageSize = thisObject.currentImageSize - 10 } } // The fontSize also have a target. - if (Math.abs(thisObject.currentFontSize - thisObject.targetFontSize) >= 0.2) { + if (Math.abs(thisObject.currentFontSize - thisObject.targetFontSize) >= 1) { if (thisObject.currentFontSize < thisObject.targetFontSize) { - thisObject.currentFontSize = thisObject.currentFontSize + 0.2 + thisObject.currentFontSize = thisObject.currentFontSize + 1 } else { - thisObject.currentFontSize = thisObject.currentFontSize - 0.2 + thisObject.currentFontSize = thisObject.currentFontSize - 1 } } @@ -307,3 +309,4 @@ function newFloatingObject () { function updateRadius () { } } + diff --git a/FloatingSpace/StrategyPart.js b/FloatingSpace/StrategyPart.js index aab937faa..664ec9fc8 100644 --- a/FloatingSpace/StrategyPart.js +++ b/FloatingSpace/StrategyPart.js @@ -16,6 +16,11 @@ function newStrategyPart () { payload: undefined, codeEditor: undefined, partTitle: undefined, + isExecuting: undefined, + getReadyToAttach: getReadyToAttach, + showAvailabilityToAttach: showAvailabilityToAttach, + highlight: highlight, + unHighlight: unHighlight, physics: physics, drawBackground: drawBackground, drawMiddleground: drawMiddleground, @@ -35,10 +40,30 @@ function newStrategyPart () { thisObject.container.frame.position.y = 0 let icon + let executingIcon let selfFocusEventSubscriptionId let selfNotFocuskEventSubscriptionId let selfDisplaceEventSubscriptionId + let selfDragStartedEventSubscriptionId + let selfDragFinishedEventSubscriptionId + + let isHighlighted + let highlightCounter = 0 + + let previousDistance + + let readyToAttachDisplayCounter = 5 + let readyToAttachDisplayIncrement = 0.1 + + let readyToAttachCounter = 0 + let isReadyToAttach + let availableToAttachCounter = 0 + let isAvailableToAttach + + let isAttaching = false + let isDragging = false + let attachToNode return thisObject @@ -46,6 +71,8 @@ function newStrategyPart () { thisObject.container.eventHandler.stopListening(selfFocusEventSubscriptionId) thisObject.container.eventHandler.stopListening(selfNotFocuskEventSubscriptionId) thisObject.container.eventHandler.stopListening(selfDisplaceEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfDragStartedEventSubscriptionId) + thisObject.container.eventHandler.stopListening(selfDragFinishedEventSubscriptionId) thisObject.container.finalize() thisObject.container = undefined @@ -63,6 +90,7 @@ function newStrategyPart () { } icon = undefined + attachToNode = undefined } function initialize (payload, menuItemsInitialValues) { @@ -88,22 +116,26 @@ function newStrategyPart () { selfFocusEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onFocus', onFocus) selfNotFocuskEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onNotFocus', onNotFocus) selfDisplaceEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDisplace', onDisplace) + selfDragStartedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDragStarted', onDragStarted) + selfDragFinishedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDragFinished', onDragFinished) } function getContainer (point) { let container - if (thisObject.codeEditor !== undefined) { - container = thisObject.codeEditor.getContainer(point) + if (isDragging === false && thisObject.isOnFocus === true) { + if (thisObject.codeEditor !== undefined) { + container = thisObject.codeEditor.getContainer(point) + if (container !== undefined) { return container } + } + + container = thisObject.partTitle.getContainer(point) + if (container !== undefined) { return container } + + container = thisObject.menu.getContainer(point) if (container !== undefined) { return container } } - container = thisObject.partTitle.getContainer(point) - if (container !== undefined) { return container } - - container = thisObject.menu.getContainer(point) - if (container !== undefined) { return container } - if (thisObject.container.frame.isThisPointHere(point, true) === true) { return thisObject.container } else { @@ -120,18 +152,149 @@ function newStrategyPart () { } if (thisObject.payload.chainParent === undefined) { - thisObject.payload.targetPosition.x = canvas.floatingSpace.container.frame.width / 2, - thisObject.payload.targetPosition.y = canvas.floatingSpace.container.frame.height / 2 + thisObject.payload.targetPosition.x = thisObject.payload.position.x, + thisObject.payload.targetPosition.y = thisObject.payload.position.y } else { thisObject.payload.targetPosition.x = thisObject.payload.chainParent.payload.position.x thisObject.payload.targetPosition.y = thisObject.payload.chainParent.payload.position.y } iconPhysics() + highlightPhisycs() + detachingPhysics() + attachingPhysics() + } + + function attachingPhysics () { + attacchingCounters() + + if (thisObject.isOnFocus !== true) { return } + if (isDragging !== true) { return } + if (thisObject.payload.chainParent !== undefined) { return } + + let nearbyFloatingObjects = thisObject.payload.floatingObject.nearbyFloatingObjects + let compatibleType + let compatibleSubType + switch (thisObject.payload.node.type) { + case 'Strategy': + compatibleType = 'Trading System' + compatibleSubType = undefined + break + case 'Phase': + compatibleType = 'Stop' + '.' + 'Take Profit' + '.' + 'Phase' + compatibleSubType = undefined + break + case 'Situation': + compatibleType = 'Phase' + '.' + 'Take Position Event' + '.' + 'Trigger On Event' + '.' + 'Trigger Off Event' + compatibleSubType = undefined + break + case 'Condition': + compatibleType = 'Situation' + compatibleSubType = undefined + break + default: + return + } + let foundCompatible = false + attachToNode = undefined + isAttaching = false + + for (let i = 0; i < nearbyFloatingObjects.length; i++) { + let nearby = nearbyFloatingObjects[i] + let distance = nearby[0] + let floatingObject = nearby[1] + let nearbyNode = floatingObject.payload.node + if (compatibleType.indexOf(nearbyNode.type) >= 0) { + if (thisObject.payload.node.type === 'Phase' && nearbyNode.type === 'Phase' && nearbyNode.payload.parentNode === undefined) { continue } + if (foundCompatible === false) { + if (distance < thisObject.container.frame.radius * 1.5 + floatingObject.container.frame.radius * 1.5) { + nearbyNode.payload.uiObject.getReadyToAttach() + isAttaching = true + attachToNode = nearbyNode + foundCompatible = true + } + } + nearbyNode.payload.uiObject.showAvailabilityToAttach() + } + } + } + + function attacchingCounters () { + if (readyToAttachDisplayCounter > 15) { + readyToAttachDisplayIncrement = -0.25 + } + + if (readyToAttachDisplayCounter < 5) { + readyToAttachDisplayIncrement = 0.25 + } + + readyToAttachDisplayCounter = readyToAttachDisplayCounter + readyToAttachDisplayIncrement + + readyToAttachCounter-- + if (readyToAttachCounter <= 0) { + readyToAttachCounter = 0 + isReadyToAttach = false + } else { + isReadyToAttach = true + } + + availableToAttachCounter-- + if (availableToAttachCounter <= 0) { + availableToAttachCounter = 0 + isAvailableToAttach = false + } else { + isAvailableToAttach = true + } + } + + function getReadyToAttach () { + readyToAttachCounter = 10 + } + + function showAvailabilityToAttach () { + availableToAttachCounter = 10 + } + + function detachingPhysics () { + if (isDragging !== true) { return } + + let distanceToChainParent = Math.sqrt(Math.pow(thisObject.payload.position.x - thisObject.payload.targetPosition.x, 2) + Math.pow(thisObject.payload.position.y - thisObject.payload.targetPosition.y, 2)) + let ratio = distanceToChainParent / previousDistance + previousDistance = distanceToChainParent + if (thisObject.isOnFocus !== true) { return } + if (thisObject.payload.chainParent === undefined) { return } + if (thisObject.payload.parentNode === undefined) { return } + + let THRESHOLD = 1.15 + + if (previousDistance !== undefined) { + if (ratio > THRESHOLD) { + canvas.strategySpace.workspace.detachNode(thisObject.payload.node) + } + } + } + + function highlightPhisycs () { + highlightCounter-- + if (highlightCounter < 0) { + highlightCounter = 0 + isHighlighted = false + } + } + + function highlight () { + isHighlighted = true + highlightCounter = 30 + } + + function unHighlight () { + // isHighlighted = false + // highlightCounter = 0 } function iconPhysics () { icon = canvas.strategySpace.iconByPartType.get(thisObject.payload.node.type) + executingIcon = canvas.strategySpace.iconCollection.get('attractive') } function onFocus () { @@ -149,10 +312,28 @@ function newStrategyPart () { thisObject.partTitle.exitEditMode() } + function onDragStarted (event) { + thisObject.partTitle.exitEditMode() + if (thisObject.codeEditor !== undefined) { + thisObject.codeEditor.deactivate() + } + isDragging = true + } + + function onDragFinished (event) { + if (isAttaching === true) { + canvas.strategySpace.workspace.attachNode(thisObject.payload.node, attachToNode) + } + isDragging = false + } + function drawBackground () { if (thisObject.isOnFocus === false) { drawConnectingLine() - thisObject.menu.drawBackground() + + if (isDragging === false && thisObject.isOnFocus === true) { + thisObject.menu.drawBackground() + } } } @@ -166,14 +347,15 @@ function newStrategyPart () { function drawForeground () { if (thisObject.isOnFocus === false) { drawBodyAndPicture() - thisObject.menu.drawForeground() + if (isDragging === false) { + thisObject.menu.drawForeground() + } } } function drawOnFocus () { if (thisObject.isOnFocus === true) { drawConnectingLine() - thisObject.menu.drawBackground() if (thisObject.codeEditor !== undefined) { thisObject.codeEditor.drawBackground() @@ -186,11 +368,17 @@ function newStrategyPart () { if (thisObject.codeEditor !== undefined) { if (thisObject.codeEditor.visible === false) { drawBodyAndPicture() - thisObject.menu.drawForeground() + if (isDragging === false) { + thisObject.menu.drawBackground() + thisObject.menu.drawForeground() + } } } else { drawBodyAndPicture() - thisObject.menu.drawForeground() + if (isDragging === false) { + thisObject.menu.drawBackground() + thisObject.menu.drawForeground() + } } } } @@ -217,9 +405,9 @@ function newStrategyPart () { browserCanvasContext.beginPath() browserCanvasContext.moveTo(position.x, position.y) browserCanvasContext.lineTo(targetPoint.x, targetPoint.y) - browserCanvasContext.strokeStyle = 'rgba(204, 204, 204, 0.5)' - browserCanvasContext.setLineDash([1, 4]) - browserCanvasContext.lineWidth = 1 + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)' + browserCanvasContext.setLineDash([3, 4]) + browserCanvasContext.lineWidth = 2 browserCanvasContext.stroke() browserCanvasContext.setLineDash([0, 0]) } @@ -232,7 +420,7 @@ function newStrategyPart () { browserCanvasContext.beginPath() browserCanvasContext.arc(targetPoint.x, targetPoint.y, radius, 0, Math.PI * 2, true) browserCanvasContext.closePath() - browserCanvasContext.fillStyle = 'rgba(30, 30, 30, 1)' + browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' browserCanvasContext.fill() } } @@ -253,7 +441,7 @@ function newStrategyPart () { let label if (radius > 6) { - const MAX_LABEL_LENGTH = 30 + const MAX_LABEL_LENGTH = 20 label = thisObject.payload.subTitle label = addIndexNumber(label) @@ -279,35 +467,50 @@ function newStrategyPart () { switch (thisObject.payload.node.type) { case 'Phase': { let parent = thisObject.payload.parentNode - for (let i = 0; i < parent.phases.length; i++) { - let phase = parent.phases[i] - if (phase.id === thisObject.payload.node.id) { - label = label + ' #' + (i + 1) - return label + if (parent !== undefined) { + for (let i = 0; i < parent.phases.length; i++) { + let phase = parent.phases[i] + if (phase.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } } + } else { + return label } + break } case 'Situation': { let parent = thisObject.payload.parentNode - for (let i = 0; i < parent.situations.length; i++) { - let situation = parent.situations[i] - if (situation.id === thisObject.payload.node.id) { - label = label + ' #' + (i + 1) - return label + if (parent !== undefined) { + for (let i = 0; i < parent.situations.length; i++) { + let situation = parent.situations[i] + if (situation.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } } + } else { + return label } + break } case 'Condition': { let parent = thisObject.payload.parentNode - for (let i = 0; i < parent.conditions.length; i++) { - let condition = parent.conditions[i] - if (condition.id === thisObject.payload.node.id) { - label = label + ' #' + (i + 1) - return label + if (parent !== undefined) { + for (let i = 0; i < parent.conditions.length; i++) { + let condition = parent.conditions[i] + if (condition.id === thisObject.payload.node.id) { + label = label + ' #' + (i + 1) + return label + } } + } else { + return label } + break } default: { @@ -327,7 +530,7 @@ function newStrategyPart () { let radius = thisObject.container.frame.radius if (radius > 0.5) { - const VISIBLE_RADIUS = 5 + let VISIBLE_RADIUS = 5 let visiblePosition = { x: thisObject.container.frame.position.x, @@ -346,12 +549,57 @@ function newStrategyPart () { browserCanvasContext.fill() browserCanvasContext.beginPath() - browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS / 2, 0, Math.PI * 2, true) + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS - 2, 0, Math.PI * 2, true) browserCanvasContext.closePath() browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)' browserCanvasContext.fill() + + if (isHighlighted === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius + let OPACITY = highlightCounter / 30 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([4, 20]) + browserCanvasContext.stroke() + } + + if (isReadyToAttach === true) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 2 + readyToAttachDisplayCounter * 2 + let OPACITY = readyToAttachCounter / 10 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')' + + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([readyToAttachDisplayCounter, readyToAttachDisplayCounter * 2]) + browserCanvasContext.stroke() + } + + if (isAvailableToAttach === true && isReadyToAttach === false) { + VISIBLE_RADIUS = thisObject.container.frame.radius * 1.5 + let OPACITY = availableToAttachCounter / 10 + + browserCanvasContext.beginPath() + browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, 0, Math.PI * 2, true) + browserCanvasContext.closePath() + + browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TURQUOISE + ', ' + OPACITY + ')' + + browserCanvasContext.lineWidth = 10 + browserCanvasContext.setLineDash([8, 20]) + browserCanvasContext.stroke() + } } /* Image */ @@ -365,5 +613,23 @@ function newStrategyPart () { thisObject.payload.floatingObject.currentImageSize) } } + + if (thisObject.isExecuting === true) { + let a = 1 + } + if (executingIcon !== undefined) { + if (executingIcon.canDrawIcon === true) { + if (thisObject.isExecuting === true) { + const DISTANCE_FROM_CENTER = thisObject.container.frame.radius / 3 + 50 + const EXECUTING_ICON_SIZE = 20 + thisObject.container.frame.radius / 6 + + browserCanvasContext.drawImage( + executingIcon, position.x - EXECUTING_ICON_SIZE / 2, + position.y - EXECUTING_ICON_SIZE / 2 - DISTANCE_FROM_CENTER, + EXECUTING_ICON_SIZE, + EXECUTING_ICON_SIZE) + } + } + } } } diff --git a/FloatingSpace/StrategyPartConstructor.js b/FloatingSpace/StrategyPartConstructor.js index a4715cd3b..bbe0151e8 100644 --- a/FloatingSpace/StrategyPartConstructor.js +++ b/FloatingSpace/StrategyPartConstructor.js @@ -41,7 +41,7 @@ function newStrategyPartConstructor () { strategyPart.isVisibleFunction = canvas.floatingSpace.isThisPointVisible let menuItemsInitialValues = getMenuItemsInitialValues(strategyPart, floatingObject, payload) strategyPart.initialize(payload, menuItemsInitialValues) - strategyPart.container.connectToParent(floatingObject.container, false, false, true, true, false, false, true, true, true) + strategyPart.container.connectToParent(floatingObject.container, false, false, true, true, false, false, true, true, true, true, true) payload.uiObject = strategyPart setFloatingObjectBasicProperties(floatingObject, payload) @@ -71,18 +71,6 @@ function newStrategyPartConstructor () { currentRadius: 0, angle: -135 }, - { - action: 'Reload Trading System', - actionFunction: payload.onMenuItemClick, - label: 'Reload', - visible: false, - iconPathOn: 'vector', - iconPathOff: 'vector', - rawRadius: 8, - targetRadius: 0, - currentRadius: 0, - angle: -60 - }, { action: 'Save Trading System', actionFunction: payload.onMenuItemClick, @@ -96,7 +84,7 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: -20 + angle: -40 }, { action: 'New Strategy', @@ -109,19 +97,19 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 20 + angle: 0 }, { - action: 'Open Settings', + action: 'Download', actionFunction: payload.onMenuItemClick, - label: 'Settings', + label: 'Download', visible: false, - iconPathOn: 'tools', - iconPathOff: 'tools', + iconPathOn: 'upload', + iconPathOff: 'upload', rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 60 + angle: 40 }] break } @@ -153,12 +141,12 @@ function newStrategyPartConstructor () { angle: -20 }, { - action: 'Open Settings', + action: 'Download', actionFunction: payload.onMenuItemClick, - label: 'Settings', + label: 'Download', visible: false, - iconPathOn: 'tools', - iconPathOff: 'tools', + iconPathOn: 'upload', + iconPathOff: 'upload', rawRadius: 8, targetRadius: 0, currentRadius: 0, @@ -166,7 +154,7 @@ function newStrategyPartConstructor () { }] break } - case 'Strategy Entry Event': { + case 'Trigger On Event': { menuItemsInitialValues = [ { action: 'Pin / Unpin', @@ -186,17 +174,29 @@ function newStrategyPartConstructor () { actionFunction: payload.onMenuItemClick, label: 'Add Situation', visible: false, - iconPathOn: 'attractive', - iconPathOff: 'attractive', + iconPathOn: 'pyramid', + iconPathOff: 'pyramid', relatedStrategyPart: 'Situation', rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 0 + angle: -20 + }, + { + action: 'Download', + actionFunction: payload.onMenuItemClick, + label: 'Download', + visible: false, + iconPathOn: 'upload', + iconPathOff: 'upload', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 }] break } - case 'Strategy Exit Event': { + case 'Trigger Off Event': { menuItemsInitialValues = [ { action: 'Pin / Unpin', @@ -216,17 +216,29 @@ function newStrategyPartConstructor () { actionFunction: payload.onMenuItemClick, label: 'Add Situation', visible: false, - iconPathOn: 'attractive', - iconPathOff: 'attractive', + iconPathOn: 'pyramid', + iconPathOff: 'pyramid', relatedStrategyPart: 'Situation', rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 0 + angle: -20 + }, + { + action: 'Download', + actionFunction: payload.onMenuItemClick, + label: 'Download', + visible: false, + iconPathOn: 'upload', + iconPathOff: 'upload', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 }] break } - case 'Trade Entry Event': { + case 'Take Position Event': { menuItemsInitialValues = [ { action: 'Pin / Unpin', @@ -246,13 +258,25 @@ function newStrategyPartConstructor () { actionFunction: payload.onMenuItemClick, label: 'Add Situation', visible: false, - iconPathOn: 'attractive', - iconPathOff: 'attractive', + iconPathOn: 'pyramid', + iconPathOff: 'pyramid', relatedStrategyPart: 'Situation', rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 0 + angle: -20 + }, + { + action: 'Download', + actionFunction: payload.onMenuItemClick, + label: 'Download', + visible: false, + iconPathOn: 'upload', + iconPathOff: 'upload', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 }] break } @@ -282,7 +306,19 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 0 + angle: -20 + }, + { + action: 'Download', + actionFunction: payload.onMenuItemClick, + label: 'Download', + visible: false, + iconPathOn: 'upload', + iconPathOff: 'upload', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 }] break } @@ -312,7 +348,19 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 0 + angle: -20 + }, + { + action: 'Download', + actionFunction: payload.onMenuItemClick, + label: 'Download', + visible: false, + iconPathOn: 'upload', + iconPathOff: 'upload', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 20 }] break } @@ -346,20 +394,21 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 40 + angle: -60, + dontShowAtFullscreen: true }, { action: 'Add Situation', actionFunction: payload.onMenuItemClick, label: 'Add Situation', visible: false, - iconPathOn: 'attractive', - iconPathOff: 'attractive', + iconPathOn: 'pyramid', + iconPathOff: 'pyramid', relatedStrategyPart: 'Situation', rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 0 + angle: -20 }, { action: 'Delete Phase', @@ -371,7 +420,19 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: -40 + angle: 20 + }, + { + action: 'Download', + actionFunction: payload.onMenuItemClick, + label: 'Download', + visible: false, + iconPathOn: 'upload', + iconPathOff: 'upload', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 60 }] break } @@ -401,7 +462,7 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 20 + angle: -40 }, { action: 'Delete Situation', @@ -413,7 +474,19 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: -20 + angle: 0 + }, + { + action: 'Download', + actionFunction: payload.onMenuItemClick, + label: 'Download', + visible: false, + iconPathOn: 'upload', + iconPathOff: 'upload', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 }] break } @@ -447,7 +520,8 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: 20 + angle: -40, + dontShowAtFullscreen: true }, { action: 'Delete Condition', @@ -459,7 +533,19 @@ function newStrategyPartConstructor () { rawRadius: 8, targetRadius: 0, currentRadius: 0, - angle: -20 + angle: 0 + }, + { + action: 'Download', + actionFunction: payload.onMenuItemClick, + label: 'Download', + visible: false, + iconPathOn: 'upload', + iconPathOff: 'upload', + rawRadius: 8, + targetRadius: 0, + currentRadius: 0, + angle: 40 }] break } @@ -483,15 +569,15 @@ function newStrategyPartConstructor () { level_1() break } - case 'Strategy Entry Event': { + case 'Trigger On Event': { level_2() break } - case 'Strategy Exit Event': { + case 'Trigger Off Event': { level_2() break } - case 'Trade Entry Event': { + case 'Take Position Event': { level_2() break } diff --git a/FloatingSpace/StrategyPartTitle.js b/FloatingSpace/StrategyPartTitle.js index 8597bff14..296b05772 100644 --- a/FloatingSpace/StrategyPartTitle.js +++ b/FloatingSpace/StrategyPartTitle.js @@ -55,13 +55,13 @@ function newStrategyPartTitle () { thisObject.allwaysVisible = true break } - case 'Strategy Entry Event': { + case 'Trigger On Event': { break } - case 'Strategy Exit Event': { + case 'Trigger Off Event': { break } - case 'Trade Entry Event': { + case 'Take Position Event': { break } case 'Stop': { @@ -90,6 +90,8 @@ function newStrategyPartTitle () { } function getContainer (point) { + if (CURRENT_TOP_MARGIN === 0) { return } // Fullscreen mode does not allow INPUT elements + let container if (thisObject.editMode === true) { @@ -108,6 +110,7 @@ function newStrategyPartTitle () { } function physics () { + if (thisObject.payload.title === undefined) { return } let title = trimTitle(thisObject.payload.title) const FRAME_HEIGHT = 25 @@ -121,12 +124,12 @@ function newStrategyPartTitle () { if (thisObject.editMode === true) { let inputPosition = { x: 0, - y: 0 + window.canvasApp.topMargin + y: 0 + CURRENT_TOP_MARGIN } inputPosition = thisObject.container.frame.frameThisPoint(inputPosition) - if (inputPosition.y < window.canvasApp.topMargin) { exitEditMode() } + if (inputPosition.y < CURRENT_TOP_MARGIN) { exitEditMode() } let inputDiv = document.getElementById('inputDiv') inputDiv.style = 'position:absolute; top:' + inputPosition.y + 'px; left:' + inputPosition.x + 'px; z-index:1; ' @@ -250,7 +253,8 @@ function newStrategyPartTitle () { } function trimTitle (title) { - const MAX_LABEL_LENGTH = 25 + if (title === undefined) { return } + const MAX_LABEL_LENGTH = 35 if (title.length > MAX_LABEL_LENGTH) { title = title.substring(0, MAX_LABEL_LENGTH) + '...' } diff --git a/Globals.js b/Globals.js index d25a99d1c..b458ec7bc 100644 --- a/Globals.js +++ b/Globals.js @@ -165,6 +165,8 @@ let INITIAL_ZOOM_LEVEL = -28.25 // This is the zoom level at the view port let INITIAL_TIME_PERIOD = ONE_DAY_IN_MILISECONDS // This value will be overwritten at the viewPort.initialize if the user had a prevous session with this same browser. let VERY_LARGE_NUMBER = 100000000000 +let CURRENT_TOP_MARGIN = window.canvasApp.topMargin + let maxDate = new Date(); maxDate.setMilliseconds(0); maxDate.setDate(maxDate.getDate() + 365 * 1); // We might have charts that projects data into the future. diff --git a/Panels/ProductCard.js b/Panels/ProductCard.js index f98d94155..6f1321131 100644 --- a/Panels/ProductCard.js +++ b/Panels/ProductCard.js @@ -1,22 +1,20 @@ function newProductCard () { - const CONSOLE_LOG = false - const MODULE_NAME = 'Product Card' - const INFO_LOG = false const ERROR_LOG = true - const INTENSIVE_LOG = false const logger = newWebDebugLog() logger.fileName = MODULE_NAME let thisObject = { container: undefined, draw: draw, - status: 'off', + status: PRODUCT_CARD_STATUS.OFF, devTeam: undefined, bot: undefined, product: undefined, code: undefined, fitFunction: undefined, + turnOff: turnOff, + turnOn: turnOn, setDatetime: setDatetime, setTimePeriod: setTimePeriod, onMarketFileLoaded: onMarketFileLoaded, @@ -109,8 +107,6 @@ function newProductCard () { function initialize () { /* Create this objects continer */ try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> Entering function.') } - thisObject.container = newContainer() thisObject.container.initialize(MODULE_NAME + thisObject.code) thisObject.container.detectMouseOver = true @@ -159,7 +155,7 @@ function newProductCard () { */ - const DEFAULT_ON_PRODUCT = 'AAMasters-AAOlivia-Candles' + const DEFAULT_ON_PRODUCT = 'Poloniex-BTC/USDT-AAMasters-AAOlivia-Candles' if (thisObject.code === DEFAULT_ON_PRODUCT) { changeStatusTo(PRODUCT_CARD_STATUS.LOADING) @@ -170,7 +166,7 @@ function newProductCard () { /* Lets listen to our own events to react when we have a Mouse Click */ - thisObject.container.eventHandler.listenToEvent('onMouseClick', buttonPressed) + thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver) /* WARNING THIS IS TEMPORARY CODE */ @@ -285,10 +281,6 @@ function newProductCard () { if (currentDate !== newDate) { if (timePeriod <= _1_HOUR_IN_MILISECONDS) { dailyFileProgressBar.animatedValue = 0 - - if (CONSOLE_LOG === true) { - console.log('ProductCard -> onDayChanged -> dailyFileProgressBar.animatedValue = ' + dailyFileProgressBar.animatedValue) - } } } } @@ -306,24 +298,16 @@ function newProductCard () { if (timePeriod <= _1_HOUR_IN_MILISECONDS) { dailyFileProgressBar.animatedValue = 0 - - if (CONSOLE_LOG === true) { - console.log('ProductCard -> onTimePeriodChanged -> dailyFileProgressBar.animatedValue = ' + dailyFileProgressBar.animatedValue) - } } } } function onMarketFileLoaded (event) { - marketFileProgressBar.value = Math.trunc(event.currentValue * 100 / event.totalValue) + marketFileProgressBar.value = Math.trunc(event.totalValue * 100 / event.totalValue) marketFileProgressBar.fillStyle = LOADING_FILL_STYLE marketFileProgressBar.strokeStyle = LOADING_STROKE_STYLE if (marketFileProgressBar.value > 100) { marketFileProgressBar.value = 100 } - - if (CONSOLE_LOG === true) { - console.log('ProductCard onMarketFileLoaded Value = ' + marketFileProgressBar.value + '% for ' + thisObject.code + '. Event = ' + JSON.stringify(event)) - } } function onDailyFileLoaded (event) { @@ -332,10 +316,6 @@ function newProductCard () { dailyFileProgressBar.strokeStyle = LOADING_STROKE_STYLE if (dailyFileProgressBar.value > 100) { dailyFileProgressBar.value = 100 } - - if (CONSOLE_LOG === true) { - console.log('ProductCard onDailyFileLoaded Value = ' + dailyFileProgressBar.value + '% for ' + thisObject.code + '. Event = ' + JSON.stringify(event)) - } } function onSingleFileLoaded (event) { @@ -344,10 +324,6 @@ function newProductCard () { singleFileProgressBar.strokeStyle = LOADING_STROKE_STYLE if (singleFileProgressBar.value > 100) { singleFileProgressBar.value = 100 } - - if (CONSOLE_LOG === true) { - console.log('ProductCard onSingleFileLoaded Value = ' + singleFileProgressBar.value + '% for ' + thisObject.code + '. Event = ' + JSON.stringify(event)) - } } function onFileSequenceLoaded (event) { @@ -356,50 +332,55 @@ function newProductCard () { fileSequenceProgressBar.strokeStyle = LOADING_STROKE_STYLE if (fileSequenceProgressBar.value > 100) { fileSequenceProgressBar.value = 100 } + } - if (CONSOLE_LOG === true) { - console.log('ProductCard onFileSequenceLoaded Value = ' + fileSequenceProgressBar.value + '% for ' + thisObject.code + '. Event = ' + JSON.stringify(event)) + function turnOff () { + if (thisObject.status !== PRODUCT_CARD_STATUS.OFF) { + resetProgressBars() + changeStatusTo(PRODUCT_CARD_STATUS.OFF) } } - function buttonPressed (event) { - switch (thisObject.status) { - - case PRODUCT_CARD_STATUS.ON: - - changeStatusTo(PRODUCT_CARD_STATUS.OFF) - - marketFileProgressBar.animatedValue = 0 - marketFileProgressBar.value = 0 - marketFileProgressBar.fillStyle = UNLOADED_FILL_STYLE - marketFileProgressBar.strokeStyle = UNLOADED_STROKE_STYLE - - dailyFileProgressBar.animatedValue = 0 - dailyFileProgressBar.value = 0 - dailyFileProgressBar.fillStyle = UNLOADED_FILL_STYLE - dailyFileProgressBar.strokeStyle = UNLOADED_STROKE_STYLE - - singleFileProgressBar.animatedValue = 0 - singleFileProgressBar.value = 0 - singleFileProgressBar.fillStyle = UNLOADED_FILL_STYLE - singleFileProgressBar.strokeStyle = UNLOADED_STROKE_STYLE - - fileSequenceProgressBar.animatedValue = 0 - fileSequenceProgressBar.value = 0 - fileSequenceProgressBar.fillStyle = UNLOADED_FILL_STYLE - fileSequenceProgressBar.strokeStyle = UNLOADED_STROKE_STYLE - - break - - case PRODUCT_CARD_STATUS.OFF: - - changeStatusTo(PRODUCT_CARD_STATUS.LOADING) - - break - + function turnOn () { + if (thisObject.status === PRODUCT_CARD_STATUS.OFF) { + changeStatusTo(PRODUCT_CARD_STATUS.LOADING) } } + function onMouseClick (event) { + switch (thisObject.status) { + case PRODUCT_CARD_STATUS.ON: + resetProgressBars() + changeStatusTo(PRODUCT_CARD_STATUS.OFF) + break + case PRODUCT_CARD_STATUS.OFF: + changeStatusTo(PRODUCT_CARD_STATUS.LOADING) + break + } + } + + function resetProgressBars () { + marketFileProgressBar.animatedValue = 0 + marketFileProgressBar.value = 0 + marketFileProgressBar.fillStyle = UNLOADED_FILL_STYLE + marketFileProgressBar.strokeStyle = UNLOADED_STROKE_STYLE + + dailyFileProgressBar.animatedValue = 0 + dailyFileProgressBar.value = 0 + dailyFileProgressBar.fillStyle = UNLOADED_FILL_STYLE + dailyFileProgressBar.strokeStyle = UNLOADED_STROKE_STYLE + + singleFileProgressBar.animatedValue = 0 + singleFileProgressBar.value = 0 + singleFileProgressBar.fillStyle = UNLOADED_FILL_STYLE + singleFileProgressBar.strokeStyle = UNLOADED_STROKE_STYLE + + fileSequenceProgressBar.animatedValue = 0 + fileSequenceProgressBar.value = 0 + fileSequenceProgressBar.fillStyle = UNLOADED_FILL_STYLE + fileSequenceProgressBar.strokeStyle = UNLOADED_STROKE_STYLE + } + function onMouseOver (event) { lastMouseOver = 25 } @@ -407,11 +388,8 @@ function newProductCard () { function changeStatusTo (pNewStatus) { if (thisObject.status !== pNewStatus) { thisObject.status = pNewStatus - let eventData = thisObject - thisObject.container.eventHandler.raiseEvent('Status Changed', eventData) - window.localStorage.setItem(thisObject.code, thisObject.status) } } @@ -422,9 +400,7 @@ function newProductCard () { function drawProductCard () { /* - Put images on the card. - */ let fitImagePoint = { @@ -443,30 +419,23 @@ function newProductCard () { if (lastMouseOver > 0) { /* First the Dev Team Profile Picture. */ - let teamImagePoint = { x: 2, y: thisObject.container.frame.height / 2 - devTeamImageSize / 2 } teamImagePoint = thisObject.container.frame.frameThisPoint(teamImagePoint) - fitImagePoint = thisObject.fitFunction(teamImagePoint) - let teamImage - if (legacyTeamAvatarLoaded === true) { teamImage = legacyTeamAvatar } - if (teamAvatarLoaded === true) { teamImage = teamAvatar } - if (teamImage !== undefined && teamImagePoint.y === fitImagePoint.y) { if (teamImage.naturalHeight !== 0) { /* The image is rounded before being displayed. */ - browserCanvasContext.save() browserCanvasContext.beginPath() browserCanvasContext.arc(teamImagePoint.x + devTeamImageSize / 2, teamImagePoint.y + devTeamImageSize / 2, devTeamImageSize / 2, 0, Math.PI * 2, true) @@ -482,27 +451,21 @@ function newProductCard () { } /* Second the Bot's Profile Picture. */ - if (thisObject.bot.profilePicture !== undefined) { let botImagePoint = { x: thisObject.container.frame.width - botImageSize / 2 - 8, y: thisObject.container.frame.height / 2 - botImageSize / 2 } - botImagePoint = thisObject.container.frame.frameThisPoint(botImagePoint) fitImagePoint = thisObject.fitFunction(botImagePoint) let imageId = thisObject.bot.devTeam + '.' + thisObject.bot.profilePicture - /* TODO Temporary code */ let botImage - botImage = thisObject.bot.avatar - if (botImage !== undefined && botImagePoint.y === fitImagePoint.y) { if (botImage.naturalHeight !== 0) { /* The image is rounded before being displayed. */ - browserCanvasContext.save() browserCanvasContext.beginPath() browserCanvasContext.arc(botImagePoint.x + botImageSize / 2, botImagePoint.y + botImageSize / 2, botImageSize / 2, 0, Math.PI * 2, true) @@ -518,33 +481,26 @@ function newProductCard () { } } } - /* Third the Plotter's Profile Picture. */ - if (thisObject.product.plotter.profilePicture !== undefined) { let plotterImagePoint = { x: thisObject.container.frame.width / 2 - plotterImageSize.width / 2, y: thisObject.container.frame.height / 2 - plotterImageSize.height / 2 } - plotterImagePoint = thisObject.container.frame.frameThisPoint(plotterImagePoint) fitImagePoint = { x: 0, y: plotterImagePoint.y + plotterImageSize.height } - fitImagePoint = thisObject.fitFunction(fitImagePoint) - let imageId = thisObject.product.plotter.devTeam + '.' + thisObject.product.plotter.codeName + '.' + thisObject.product.plotter.moduleName + '.' + thisObject.product.plotter.profilePicture let plotterImage = legacyPlotterBanner - if (plotterImage !== undefined && plotterImagePoint.y + plotterImageSize.height === fitImagePoint.y) { if (plotterImage.naturalHeight !== 0) { browserCanvasContext.drawImage(plotterImage, plotterImagePoint.x, plotterImagePoint.y, plotterImageSize.width, plotterImageSize.height) } } } - drawProductLoadStatus() function drawProductLoadStatus () { @@ -553,19 +509,15 @@ function newProductCard () { let loadingFillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 0.40)' switch (thisObject.status) { - case PRODUCT_CARD_STATUS.ON: browserCanvasContext.fillStyle = onFillStyle break - case PRODUCT_CARD_STATUS.OFF: browserCanvasContext.fillStyle = offFillStyle break - case PRODUCT_CARD_STATUS.LOADING: browserCanvasContext.fillStyle = loadingFillStyle break - } let centerPoint = { @@ -592,9 +544,7 @@ function newProductCard () { x: centerPoint.x - plotterImageSize.width / 2, y: centerPoint.y - plotterImageSize.height / 2 + 7 } - /* Now the transformations. */ - point1 = thisObject.container.frame.frameThisPoint(point1) point2 = thisObject.container.frame.frameThisPoint(point2) point3 = thisObject.container.frame.frameThisPoint(point3) @@ -611,28 +561,21 @@ function newProductCard () { browserCanvasContext.lineTo(point3.x, point3.y) browserCanvasContext.lineTo(point4.x, point4.y) browserCanvasContext.closePath() - browserCanvasContext.fill() - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 1)' browserCanvasContext.lineWidth = 0.1 browserCanvasContext.stroke() } - /* print the text */ - let labelPoint let fontSize = 10 browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY - let label - if (lastMouseOver > 0) { /* devTeam */ - label = thisObject.devTeam.displayName if (label.length > 10) { label = label.substring(1, 8) + '...' } @@ -643,13 +586,10 @@ function newProductCard () { labelPoint = thisObject.container.frame.frameThisPoint(labelPoint) labelPoint = thisObject.fitFunction(labelPoint) - browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)' browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) - /* bot */ - label = thisObject.bot.displayName if (label.length > 10) { label = label.substring(1, 8) + '...' } @@ -665,23 +605,17 @@ function newProductCard () { browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)' browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y) } - /* product */ - fontSize = 10 - label = thisObject.product.displayName - labelPoint = { x: 65, y: thisObject.container.frame.height / 2 + 15 } - labelPoint = { x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO, y: thisObject.container.frame.height / 2 - devTeamImageSize / 2 - fontSize * FONT_ASPECT_RATIO - 20 } - labelPoint = thisObject.container.frame.frameThisPoint(labelPoint) labelPoint = thisObject.fitFunction(labelPoint) @@ -969,3 +903,4 @@ function newProductCard () { browserCanvasContext.stroke() } } + diff --git a/Panels/ProductsPanel.js b/Panels/ProductsPanel.js index d570d778f..8c0cb96f3 100644 --- a/Panels/ProductsPanel.js +++ b/Panels/ProductsPanel.js @@ -1,7 +1,9 @@ function newProductsPanel () { let thisObject = { + name: 'Products Panel', fitFunction: undefined, container: undefined, + productCards: [], getLoadingProductCards: getLoadingProductCards, draw: draw, getContainer: getContainer, // returns the inner most container that holds the point received by parameter. @@ -20,7 +22,6 @@ function newProductsPanel () { thisObject.container = container let isInitialized = false - let productCards = [] let visibleProductCards = [] let firstVisibleCard = 1 @@ -65,7 +66,7 @@ function newProductsPanel () { userTeams = JSON.parse(storedTeams) } - /* First thing is to build the productCards array */ + /* First thing is to build the thisObject.productCards array */ let devTeams = ecosystem.getTeams() @@ -119,7 +120,7 @@ function newProductsPanel () { /* Add to the Product Array */ - productCards.push(productCard) + thisObject.productCards.push(productCard) /* Add to Visible Product Array */ @@ -153,14 +154,14 @@ function newProductsPanel () { let availableSlots = visibleProductCards.length if (firstVisibleCard < 1) { firstVisibleCard = 1 } - if (firstVisibleCard > (productCards.length - availableSlots + 1)) { firstVisibleCard = productCards.length - availableSlots + 1 } + if (firstVisibleCard > (thisObject.productCards.length - availableSlots + 1)) { firstVisibleCard = thisObject.productCards.length - availableSlots + 1 } visibleProductCards = [] let lastY = 5 - for (let i = 0; i < productCards.length; i++) { + for (let i = 0; i < thisObject.productCards.length; i++) { if (i + 1 >= firstVisibleCard && i + 1 < firstVisibleCard + availableSlots) { - let productCard = productCards[i] + let productCard = thisObject.productCards[i] /* Positioning within thisObject Panel */ @@ -185,13 +186,13 @@ function newProductsPanel () { } function getLoadingProductCards () { - /* Returns all productCards which status is LOADING */ + /* Returns all thisObject.productCards which status is LOADING */ let onProducts = [] - for (let i = 0; i < productCards.length; i++) { - if (productCards[i].status === PRODUCT_CARD_STATUS.LOADING) { - onProducts.push(productCards[i]) + for (let i = 0; i < thisObject.productCards.length; i++) { + if (thisObject.productCards[i].status === PRODUCT_CARD_STATUS.LOADING) { + onProducts.push(thisObject.productCards[i]) } } @@ -253,16 +254,16 @@ function newProductsPanel () { panelTabButton.draw() } - function isUserTeam(team, userTeams){ - if(userTeams === undefined) + function isUserTeam (team, userTeams) { + if (userTeams === undefined) { return false + } for (let index = 0; index < userTeams.length; index++) { - if(team === userTeams[index].slug){ + if (team === userTeams[index].slug) { return true } } return false } } - diff --git a/ProductStorage.js b/ProductStorage.js index bc0d98456..acd829022 100644 --- a/ProductStorage.js +++ b/ProductStorage.js @@ -1,7 +1,6 @@  function newProductStorage (pName) { const MODULE_NAME = 'Product Storage' - const INFO_LOG = false const ERROR_LOG = true const logger = newWebDebugLog() logger.fileName = MODULE_NAME @@ -26,19 +25,15 @@ function newProductStorage (pName) { */ let thisObject = { - + eventHandler: undefined, marketFiles: [], dailyFiles: [], singleFile: [], fileSequences: [], - setDatetime: setDatetime, setTimePeriod: setTimePeriod, - - eventHandler: undefined, initialize: initialize, finalize: finalize - } thisObject.eventHandler = newEventHandler() @@ -54,8 +49,6 @@ function newProductStorage (pName) { function finalize () { try { - if (INFO_LOG === true) { logger.write('[INFO] finalize -> Entering function.') } - for (let i = 0; i < thisObject.marketFiles.length; i++) { let marketFile = thisObject.marketFiles[i] marketFile.finalize() @@ -83,29 +76,19 @@ function newProductStorage (pName) { function initialize (pDevTeam, pBot, pProduct, pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> Entering function.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - datetime = pDatetime timePeriod = pTimePeriod let dataSetsToLoad = 0 let dataSetsLoaded = 0 - if (INFO_LOG === true) { logger.write('[INFO] initialize -> dataSetsToLoad = ' + dataSetsToLoad) } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> dataSetsLoaded = ' + dataSetsLoaded) } - for (let i = 0; i < pProduct.dataSets.length; i++) { let thisSet = pProduct.dataSets[i] switch (thisSet.type) { case 'Market Files': { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> Market Files -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - dataSetsToLoad++ - if (INFO_LOG === true) { logger.write('[INFO] initialize -> dataSetsToLoad = ' + dataSetsToLoad) } - let marketFiles = newMarketFiles() marketFiles.initialize(pDevTeam, pBot, pProduct, thisSet, pExchange, pMarket, onMarketFileReady) thisObject.marketFiles.push(marketFiles) @@ -113,12 +96,8 @@ function newProductStorage (pName) { break case 'Daily Files': { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> Daily Files -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - dataSetsToLoad++ - if (INFO_LOG === true) { logger.write('[INFO] initialize -> dataSetsToLoad = ' + dataSetsToLoad) } - let dailyFiles = newDailyFiles() dailyFiles.initialize(pDevTeam, pBot, pProduct, thisSet, pExchange, pMarket, pDatetime, pTimePeriod, onDailyFileReady) thisObject.dailyFiles.push(dailyFiles) @@ -126,12 +105,8 @@ function newProductStorage (pName) { break case 'Single File': { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> Single File -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - dataSetsToLoad++ - if (INFO_LOG === true) { logger.write('[INFO] initialize -> dataSetsToLoad = ' + dataSetsToLoad) } - let singleFile = newSingleFile() singleFile.initialize(pDevTeam, pBot, pProduct, thisSet, pExchange, pMarket, onSingleFileReady) thisObject.singleFile.push(singleFile) @@ -139,12 +114,8 @@ function newProductStorage (pName) { break case 'File Sequence': { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> File Sequence -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - dataSetsToLoad++ - if (INFO_LOG === true) { logger.write('[INFO] initialize -> dataSetsToLoad = ' + dataSetsToLoad) } - let fileSequences = newFileSequence() fileSequences.initialize(pDevTeam, pBot, pProduct, thisSet, pExchange, pMarket, onFileSequenceReady) thisObject.fileSequences.push(fileSequences) @@ -154,36 +125,15 @@ function newProductStorage (pName) { function onMarketFileReady (err, pCaller) { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> Entering function.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> Received OK Response.') } break } - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> Received FAIL Response.') } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> Received CUSTOM FAIL Response.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> err= ' + err.stack) } - - callBackFunction(err) - return - } - - default: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> Received Unexpected Response.') } - callBackFunction(err) - return - } } - let event = { totalValue: pCaller.getExpectedFiles(), currentValue: pCaller.getFilesLoaded(), @@ -192,49 +142,44 @@ function newProductStorage (pName) { thisObject.eventHandler.raiseEvent('Market File Loaded', event) - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> event.currentValue = ' + event.currentValue) } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> event.totalValue = ' + event.totalValue) } + if (event.filesNotLoaded === event.totalValue) { + dataSetsToLoad-- + checkInitializeComplete() + return + } if (event.currentValue + event.filesNotLoaded === event.totalValue) { dataSetsLoaded++ - - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onMarketFileReady -> dataSetsLoaded = ' + dataSetsLoaded) } - checkInitializeComplete() } } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onMarketFileReady -> err = ' + err.stack) } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) } } function onDailyFileReady (err, pCaller) { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> Entering function.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> Received OK Response.') } break } case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> Received FAIL Response.') } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> Received CUSTOM FAIL Response.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> err= ' + err.stack) } - - callBackFunction(err) - return + if (err.message === 'Dataset Unavailable.') { + dataSetsToLoad-- + checkInitializeComplete() + return + } else { + callBackFunction(err) + return + } } - default: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> Received Unexpected Response.') } callBackFunction(err) return } @@ -247,134 +192,88 @@ function newProductStorage (pName) { thisObject.eventHandler.raiseEvent('Daily File Loaded', event) - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> event.currentValue = ' + event.currentValue) } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> event.totalValue = ' + event.totalValue) } - if (event.currentValue === event.totalValue) { dataSetsLoaded++ - - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onDailyFileReady -> dataSetsLoaded = ' + dataSetsLoaded) } - checkInitializeComplete() } } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onDailyFileReady -> err = ' + err.stack) } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) } } function onSingleFileReady (err, pCaller) { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onSingleFileReady -> Entering function.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onSingleFileReady -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onSingleFileReady -> Received OK Response.') } break } case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onSingleFileReady -> Received FAIL Response.') } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onSingleFileReady -> Received CUSTOM FAIL Response.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onSingleFileReady -> err= ' + err.stack) } - callBackFunction(err) return } default: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onSingleFileReady -> Received Unexpected Response.') } callBackFunction(err) return } } - let event = { totalValue: 1, currentValue: 1 } - thisObject.eventHandler.raiseEvent('Single File Loaded', event) if (event.currentValue === event.totalValue) { dataSetsLoaded++ - - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onSingleFileReady -> dataSetsLoaded = ' + dataSetsLoaded) } - checkInitializeComplete() } } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onSingleFileReady -> err = ' + err.stack) } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) } } function onFileSequenceReady (err, pCaller) { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileSequenceReady -> Entering function.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileSequenceReady -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - switch (err.result) { case GLOBAL.DEFAULT_OK_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileSequenceReady -> Received OK Response.') } break } - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileSequenceReady -> Received FAIL Response.') } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) return } - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileSequenceReady -> Received CUSTOM FAIL Response.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileSequenceReady -> err= ' + err.stack) } - callBackFunction(err) return } - default: { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileSequenceReady -> Received Unexpected Response.') } callBackFunction(err) return } } - let event = { totalValue: pCaller.getExpectedFiles(), currentValue: pCaller.getFilesLoaded() } - thisObject.eventHandler.raiseEvent('File Sequence Loaded', event) if (event.currentValue === event.totalValue) { dataSetsLoaded++ - - if (INFO_LOG === true) { logger.write('[INFO] initialize -> onFileSequenceReady = ' + dataSetsLoaded) } - checkInitializeComplete() } } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> onFileSequenceReady -> err = ' + err.stack) } callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) } } function checkInitializeComplete () { try { - if (INFO_LOG === true) { logger.write('[INFO] initialize -> checkInitializeComplete -> Entering function.') } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> checkInitializeComplete -> key = ' + pDevTeam.codeName + '-' + pBot.codeName + '-' + pProduct.codeName) } - - if (INFO_LOG === true) { logger.write('[INFO] initialize -> checkInitializeComplete -> dataSetsToLoad = ' + dataSetsToLoad) } - if (INFO_LOG === true) { logger.write('[INFO] initialize -> checkInitializeComplete -> dataSetsLoaded = ' + dataSetsLoaded) } - if (dataSetsLoaded === dataSetsToLoad) { callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE) } @@ -391,8 +290,6 @@ function newProductStorage (pName) { } function setDatetime (pDatetime) { - if (INFO_LOG === true) { logger.write('[INFO] setDatetime -> Entering function.') } - /* If there is a change in the day, then we take some actions, otherwise, we dont. */ let currentDate = Math.trunc(datetime.valueOf() / ONE_DAY_IN_MILISECONDS) @@ -410,8 +307,6 @@ function newProductStorage (pName) { } function setTimePeriod (pTimePeriod) { - if (INFO_LOG === true) { logger.write('[INFO] setTimePeriod -> Entering function.') } - /* We are going to filter out the cases in which the timePeriod received is the same that the one we already know. */ if (timePeriod !== pTimePeriod) { diff --git a/README.md b/README.md index 7fa3a65e8..a99dd63b3 100644 --- a/README.md +++ b/README.md @@ -1,20 +1,42 @@ # Welcome to the Canvas App -The Canvas App is the built-in charting system of Superalgos. It is the software capable of running the Plotters that represent datasets graphically. +The Canvas App provides a visual environment for the _Charting System_ and the _Strategy Designer_. -![image](https://user-images.githubusercontent.com/9479367/56820881-520f0f80-684d-11e9-9f47-4b3ca38370e9.png) +## For Developers: Installation -## Installation +To install the Canvas App, you need to install AAWeb, following [this guide](https://github.com/Superalgos/AAWeb/blob/develop/README.md). -To install the Canvas App, you need to install AAWeb, following this guide: +## For All Users: Demo -https://github.com/Superalgos/AAWeb/blob/develop/README.md +Use the latest Chrome version to [access the Demo](https://demo.superalgos.org). -## Usage +## Overview -The first time you access the Canvas App, you will see the following screen with no chart displayed, since the app expects you to select one of the available layers at the Layers Panel on the left: +The first time you access the _Charts_, you will see the following screen, split in half. The top half features the space used by the _Charting System_ and the bottom half features the _Strategy Designer_. -![image](https://user-images.githubusercontent.com/9479367/56821029-a3b79a00-684d-11e9-8226-2471e32b70e7.png) +Use the control in the center of the turquoise bar to pull the bar up and down in order to make more room to either application. + +![image](https://user-images.githubusercontent.com/13994516/58324648-be1e6c80-7e27-11e9-906f-0f3d96c3aa7b.png) + +Pulling the bar to the very top of the screen causes the _Charting System_ to stop consuming resources in your local machine, and gives you an ample view of the _Strategy Designer_. + +The Strategy Designer allows you to manage your _Trading System_. The structure represented by the various icons nested in a hierarchy of elements is the representation of the logic behind your _Strategies_. + +![image](https://user-images.githubusercontent.com/13994516/58325421-f32bbe80-7e29-11e9-9478-9e6e4a02ae47.png) + +Pulling the bar to the very bottom of the screen causes the _Strategy Designer_ to stop consuming resources from your local machine, and offers a full-screen view of the _Charting System_. + +The _Charting System_ plots indicators data along with the actions taken by the _Strategy Engine_ and the _Executor_, integrated with market data. + +![image](https://user-images.githubusercontent.com/13994516/58325972-c37db600-7e2b-11e9-9aa2-9f6faaf8dd94.png) + +The _Strategy Engine_ backtests and forward tests the instructions defined on its _protocol_ file input. In coordination with the _Strategy Executor_, the engine can also live trade. + +The _Strategy Executor_ interprets the execution instructions embedded at a _protocol_ file. The instructions determine the type of order to use, and what to do in every possible situation that could emerge during the placement and management of exchange orders. + +The _protocol_ refers to the _Superalgos Protocol_. In conceptual terms, it is a file with a specific format capable of holding all the information concerning your _Trading System_. The _protocol_ allows us to standardize the way in which we describe strategies, so that the tools we develop to create or modify, visualize, backtest and deploy _Strategies_ can handle all _Strategies_ in a standardized fashion. It also allows _Strategies_ and even parts of _Strategies_ to be portable, along with many other features that will become possible in the future. + +In its current early version, the _protocol_ is simply the description of a JSON object which defines the desired automation. ### Layers Panel diff --git a/Scales/Commons.js b/Scales/Commons.js index b8497ca08..6e52c9c99 100644 --- a/Scales/Commons.js +++ b/Scales/Commons.js @@ -1,5 +1,5 @@ -function drawScaleDisplay (label1, label2, xExtraOffSet1, xExtraOffSet2, container) { +function drawScaleDisplay (label1, label2, xExtraOffSet1, xExtraOffSet2, container, fitFunction) { let fontSize1 = 20 let fontSize2 = 10 @@ -13,7 +13,8 @@ function drawScaleDisplay (label1, label2, xExtraOffSet1, xExtraOffSet2, contain borderColor: UI_COLOR.RUSTED_RED, castShadow: false, backgroundColor: UI_COLOR.DARK, - opacity: OPACITY + opacity: OPACITY, + fitFunction: fitFunction } roundedCornersBackground(params) @@ -28,6 +29,9 @@ function drawScaleDisplay (label1, label2, xExtraOffSet1, xExtraOffSet2, contain } labelPoint1 = container.frame.frameThisPoint(labelPoint1) + let x = labelPoint1.x + labelPoint1 = fitFunction(labelPoint1) + labelPoint1.x = x browserCanvasContext.font = fontSize1 + 'px ' + UI_FONT.PRIMARY browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' @@ -42,9 +46,11 @@ function drawScaleDisplay (label1, label2, xExtraOffSet1, xExtraOffSet2, contain } labelPoint2 = container.frame.frameThisPoint(labelPoint2) + labelPoint2 = fitFunction(labelPoint2) browserCanvasContext.font = fontSize2 + 'px ' + UI_FONT.PRIMARY browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)' browserCanvasContext.fillText(label2, labelPoint2.x, labelPoint2.y) } + diff --git a/Scales/RateScale.js b/Scales/RateScale.js index 10b6c14e1..c0d09e15c 100644 --- a/Scales/RateScale.js +++ b/Scales/RateScale.js @@ -2,10 +2,11 @@ function newRateScale () { const MODULE_NAME = 'Right Scale' let thisObject = { - heightPercentage: 100, container: undefined, rate: undefined, + fitFunction: undefined, visible: true, + heightPercentage: 100, physics: physics, draw: draw, getContainer: getContainer, @@ -34,6 +35,7 @@ function newRateScale () { function finalize () { thisObject.container.finalize() thisObject.container = undefined + thisObject.fitFunction = undefined } function initialize () { @@ -201,7 +203,7 @@ function newRateScale () { let label1 = (Math.trunc(thisObject.rate)).toLocaleString() let label2 = labelArray[1] - drawScaleDisplay(label1, label2, 10, 40, thisObject.container) + drawScaleDisplay(label1, label2, 10, 40, thisObject.container, thisObject.fitFunction) } } diff --git a/Scales/TimeScale.js b/Scales/TimeScale.js index fc0a5c2bd..ead64cf55 100644 --- a/Scales/TimeScale.js +++ b/Scales/TimeScale.js @@ -5,6 +5,7 @@ function newTimeScale () { lenghtPercentage: undefined, container: undefined, date: undefined, + fitFunction: undefined, visible: true, physics: physics, draw: draw, @@ -34,6 +35,7 @@ function newTimeScale () { function finalize () { thisObject.container.finalize() thisObject.container = undefined + thisObject.fitFunction = undefined } function initialize () { @@ -202,7 +204,7 @@ function newTimeScale () { let label1 = labelArray[1] + ' ' + labelArray[2] + ' ' + labelArray[3] let label2 = labelArray[4] - drawScaleDisplay(label1, label2, 10, 60, thisObject.container) + drawScaleDisplay(label1, label2, 10, 60, thisObject.container, thisObject.fitFunction) } } diff --git a/Spaces/ChartSpace.js b/Spaces/ChartSpace.js index e4242795a..5820e17e2 100644 --- a/Spaces/ChartSpace.js +++ b/Spaces/ChartSpace.js @@ -32,6 +32,8 @@ function newChartSpace () { thisObject.container.isDraggeable = false thisObject.container.isWheelable = false + let canvasBrowserResizedEventSubscriptionId + return thisObject function finalize () { @@ -40,6 +42,8 @@ function newChartSpace () { timeMachine.finalize() } + thisObject.container.eventHandler.stopListening(canvasBrowserResizedEventSubscriptionId) + thisObject.container.finalize() thisObject.container = undefined } @@ -48,6 +52,8 @@ function newChartSpace () { let initializedCounter = 0 let toInitialize = 1 + canvasBrowserResizedEventSubscriptionId = window.canvasApp.eventHandler.listenToEvent('Browser Resized', resize) + /* We create the first of many possible time machines that could live at the Chart Space. */ let timeMachine = newTimeMachine() @@ -57,6 +63,7 @@ function newChartSpace () { timeMachine.container.frame.position.x = thisObject.container.frame.width / 2 - timeMachine.container.frame.width / 2 timeMachine.container.frame.position.y = thisObject.container.frame.height / 2 - timeMachine.container.frame.height / 2 timeMachine.container.fitFunction = fitIntoVisibleArea + timeMachine.fitFunction = fitIntoVisibleArea timeMachine.initialize(onTimeMachineInitialized) function onTimeMachineInitialized (err) { @@ -100,6 +107,11 @@ function newChartSpace () { } } + function resize () { + thisObject.container.frame.width = browserCanvas.width + thisObject.container.frame.height = COCKPIT_SPACE_POSITION + } + function fitIntoVisibleArea (point) { /* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */ diff --git a/Spaces/CockpitSpace.js b/Spaces/CockpitSpace.js index 36d5477ff..92cb0e922 100644 --- a/Spaces/CockpitSpace.js +++ b/Spaces/CockpitSpace.js @@ -7,6 +7,9 @@ status: 'BOTTOM', assetBalances: undefined, restartSimulation: undefined, + fullscreen: undefined, + toTop: toTop, + toBottom: toBottom, draw: draw, physics: physics, getContainer: getContainer, @@ -37,9 +40,20 @@ thisObject.container = undefined thisObject.assetBalances = undefined thisObject.restartSimulation = undefined + thisObject.fullscreen = undefined } function initialize () { + thisObject.container.frame.position.x = 0 + + let INITIAL_POSITION + if (canvas.strategySpace.isInitialized === true) { + INITIAL_POSITION = 55 + } else { + INITIAL_POSITION = 100 + } + thisObject.container.frame.position.y = browserCanvas.height * INITIAL_POSITION / 100 - COCKPIT_SPACE_HEIGHT + canvasBrowserResizedEventSubscriptionId = window.canvasApp.eventHandler.listenToEvent('Browser Resized', resize) selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) @@ -49,6 +63,10 @@ thisObject.restartSimulation = newRestartSimulation() thisObject.restartSimulation.container.connectToParent(thisObject.container) thisObject.restartSimulation.initialize() + + thisObject.fullscreen = newFullScreen() + thisObject.fullscreen.container.connectToParent(thisObject.container) + thisObject.fullscreen.initialize() } function onMouseClick (event) { @@ -56,13 +74,18 @@ } function resize () { - thisObject.container.frame.position.x = 0 - thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT - thisObject.container.frame.width = browserCanvas.width thisObject.container.frame.height = COCKPIT_SPACE_HEIGHT } + function toTop () { + thisObject.container.frame.position.y = 0 + } + + function toBottom () { + thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT + } + function physics () { thisObjectPhysics() childrenPhysics() @@ -70,6 +93,7 @@ function childrenPhysics () { thisObject.restartSimulation.physics() + thisObject.fullscreen.physics() } function thisObjectPhysics () { /* Check the limits */ @@ -109,6 +133,9 @@ container = thisObject.restartSimulation.getContainer(point) if (container !== undefined) { return container } + container = thisObject.fullscreen.getContainer(point) + if (container !== undefined) { return container } + if (thisObject.container.frame.isThisPointHere(point, true) === true) { return thisObject.container } else { @@ -123,6 +150,7 @@ thisObject.assetBalances.draw() thisObject.restartSimulation.draw() + thisObject.fullscreen.draw() } function drawBackground () { diff --git a/Spaces/StrategySpace.js b/Spaces/StrategySpace.js index e4bfcca0c..c24ffadbc 100644 --- a/Spaces/StrategySpace.js +++ b/Spaces/StrategySpace.js @@ -3,10 +3,13 @@ function newStrategySpace () { const MODULE_NAME = 'Strategy Space' let thisObject = { sidePanel: undefined, - workplace: undefined, + strategizerGateway: undefined, container: undefined, iconCollection: undefined, iconByPartType: undefined, + workspace: undefined, + isDeployed: false, + isInitialized: false, draw: draw, getContainer: getContainer, makeVisible: makeVisible, @@ -26,26 +29,30 @@ function newStrategySpace () { return thisObject async function initialize () { - thisObject.workplace = newWorkspace() + thisObject.strategizerGateway = newStrategizerGateway() - thisObject.workplace.initialize() - await thisObject.workplace.loadFromStrategyzer() + thisObject.strategizerGateway.initialize() + await thisObject.strategizerGateway.loadFromStrategyzer() loadIconCollection() buildIconByPartTypeMap() + + if (thisObject.strategizerGateway.strategizerData !== undefined) { + thisObject.isInitialized = true + } } function buildIconByPartTypeMap () { const relationshipArray = [ ['Trading System', 'analysis'], ['Strategy', 'quality'], - ['Strategy Entry Event', 'startup'], - ['Strategy Exit Event', 'support'], - ['Trade Entry Event', 'compass'], + ['Trigger On Event', 'startup'], + ['Trigger Off Event', 'support'], + ['Take Position Event', 'compass'], ['Stop', 'pixel'], ['Take Profit', 'competition'], ['Phase', 'placeholder'], - ['Situation', 'attractive'], + ['Situation', 'pyramid'], ['Condition', 'testing'] ] @@ -63,6 +70,7 @@ function newStrategySpace () { 'approve', 'attach', 'attractive', + 'pyramid', 'brainstorming', 'broken-link', 'chat', @@ -131,13 +139,21 @@ function newStrategySpace () { } function makeVisible () { - if (thisObject.workplace.isDeployed !== true) { - thisObject.workplace.deploydTradingSystem() + if (thisObject.isDeployed !== true) { + deploydTradingSystem() } canvas.floatingSpace.makeVisible() visible = true } + function deploydTradingSystem () { + thisObject.workspace = newWorkspace() + if (thisObject.strategizerGateway.strategizerData !== undefined) { + thisObject.workspace.initialize(thisObject.strategizerGateway.strategizerData) + thisObject.isDeployed = true + } + } + function makeInvisible () { visible = false } @@ -145,8 +161,8 @@ function newStrategySpace () { function getContainer (point) { let container - if (thisObject.workplace !== undefined) { - container = thisObject.workplace.getContainer(point) + if (thisObject.strategizerGateway !== undefined) { + container = thisObject.strategizerGateway.getContainer(point) if (container !== undefined) { return container } } diff --git a/StrategySpace/StrategizerGateway.js b/StrategySpace/StrategizerGateway.js new file mode 100644 index 000000000..26db512ec --- /dev/null +++ b/StrategySpace/StrategizerGateway.js @@ -0,0 +1,357 @@ + +function newStrategizerGateway () { + const MODULE_NAME = 'Strategizer Gateway' + const ERROR_LOG = true + const logger = newWebDebugLog() + logger.fileName = MODULE_NAME + + let thisObject = { + strategizerData: undefined, + container: undefined, + loadFromStrategyzer: loadFromStrategyzer, + saveToStrategyzer: saveToStrategyzer, + draw: draw, + getContainer: getContainer, + initialize: initialize + } + + let colletionItems = [] + + thisObject.container = newContainer() + thisObject.container.name = MODULE_NAME + thisObject.container.initialize() + + thisObject.container.frame.width = 0 + thisObject.container.frame.height = 0 + + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 + + thisObject.container.isDraggeable = false + thisObject.container.isClickeable = false + + return thisObject + + function initialize () { + + } + + async function saveToStrategyzer (tradingSystem) { + try { + const accessToken = window.localStorage.getItem(LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY) + + let user = window.localStorage.getItem(LOGGED_IN_USER_LOCAL_STORAGE_KEY) + if (user === null) { + if (ERROR_LOG === true) { logger.write('[ERROR] saveToStrategyzer -> Can not save because user is not logged in. ') } + return + } + + user = JSON.parse(user) + + const authId = user.authId + + const apolloClient = new Apollo.lib.ApolloClient({ + networkInterface: Apollo.lib.createNetworkInterface({ + uri: window.canvasApp.graphQL.masterAppApiUrl, + transportBatching: true + }), + connectToDevTools: true + }) + + /* Updating all Strategies */ + + const networkInterface = Apollo.lib.createNetworkInterface({ + uri: window.canvasApp.graphQL.masterAppApiUrl + }) + + networkInterface.use([{ + applyMiddleware (req, next) { + req.options.headers = { + authorization: `Bearer ${accessToken}` + } + next() + } + }]) + + let dataToSave = buildGraphQLDataToSave(tradingSystem) + + const updateStrategies = () => { + return new Promise((resolve, reject) => { + apolloClient.mutate({ + mutation: GRAPHQL_MUTATION_UPDATE_STRATEGIES, + variables: { + id: thisObject.strategizerData.id, + strategy: dataToSave.strategy + } + }) + .then(response => { + resolve(true) + }) + .catch(err => { + if (ERROR_LOG === true) { logger.write('[ERROR] saveToStrategyzer -> ApolloClient error updating user strategies -> err = ' + err.stack) } + reject(err) + }) + }) + } + + let result = await updateStrategies() + + return result + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] saveToStrategyzer -> err = ' + err.stack) } + } + } + + function buildGraphQLDataToSave (tradingSystem) { + let graphQL_Mutation = '' + + let dataToSave = { + strategy: { + subStrategies: [] + } + } + + for (m = 0; m < tradingSystem.strategies.length; m++) { + let strategizerGatewayStrategy = tradingSystem.strategies[m] + let strategy = { + name: strategizerGatewayStrategy.name, + active: strategizerGatewayStrategy.active, + entryPoint: { + situations: [] + }, + exitPoint: { + situations: [] + }, + sellPoint: { + situations: [] + }, + stopLoss: { + phases: [] + }, + buyOrder: { + phases: [] + } + } + + for (let k = 0; k < strategizerGatewayStrategy.entryPoint.situations.length; k++) { + let strategizerGatewaySituation = strategizerGatewayStrategy.entryPoint.situations[k] + let situation = { + name: strategizerGatewaySituation.name, + conditions: [] + } + + for (let m = 0; m < strategizerGatewaySituation.conditions.length; m++) { + let strategizerGatewayCondition = strategizerGatewaySituation.conditions[m] + let condition = { + name: strategizerGatewayCondition.name, + code: strategizerGatewayCondition.code + } + situation.conditions.push(condition) + } + strategy.entryPoint.situations.push(situation) + } + + for (let k = 0; k < strategizerGatewayStrategy.exitPoint.situations.length; k++) { + let strategizerGatewaySituation = strategizerGatewayStrategy.exitPoint.situations[k] + let situation = { + name: strategizerGatewaySituation.name, + conditions: [] + } + + for (let m = 0; m < strategizerGatewaySituation.conditions.length; m++) { + let strategizerGatewayCondition = strategizerGatewaySituation.conditions[m] + let condition = { + name: strategizerGatewayCondition.name, + code: strategizerGatewayCondition.code + } + situation.conditions.push(condition) + } + strategy.exitPoint.situations.push(situation) + } + + for (let k = 0; k < strategizerGatewayStrategy.sellPoint.situations.length; k++) { + let strategizerGatewaySituation = strategizerGatewayStrategy.sellPoint.situations[k] + let situation = { + name: strategizerGatewaySituation.name, + conditions: [] + } + + for (let m = 0; m < strategizerGatewaySituation.conditions.length; m++) { + let strategizerGatewayCondition = strategizerGatewaySituation.conditions[m] + let condition = { + name: strategizerGatewayCondition.name, + code: strategizerGatewayCondition.code + } + situation.conditions.push(condition) + } + strategy.sellPoint.situations.push(situation) + } + + for (let p = 0; p < strategizerGatewayStrategy.stopLoss.phases.length; p++) { + let strategizerGatewayPhase = strategizerGatewayStrategy.stopLoss.phases[p] + let phase = { + name: strategizerGatewayPhase.name, + code: strategizerGatewayPhase.code, + situations: [] + } + + for (let k = 0; k < strategizerGatewayPhase.situations.length; k++) { + let strategizerGatewaySituation = strategizerGatewayPhase.situations[k] + let situation = { + name: strategizerGatewaySituation.name, + conditions: [] + } + + for (let m = 0; m < strategizerGatewaySituation.conditions.length; m++) { + let strategizerGatewayCondition = strategizerGatewaySituation.conditions[m] + let condition = { + name: strategizerGatewayCondition.name, + code: strategizerGatewayCondition.code + } + situation.conditions.push(condition) + } + phase.situations.push(situation) + } + strategy.stopLoss.phases.push(phase) + } + + for (let p = 0; p < strategizerGatewayStrategy.buyOrder.phases.length; p++) { + let strategizerGatewayPhase = strategizerGatewayStrategy.buyOrder.phases[p] + let phase = { + name: strategizerGatewayPhase.name, + code: strategizerGatewayPhase.code, + situations: [] + } + + for (let k = 0; k < strategizerGatewayPhase.situations.length; k++) { + let strategizerGatewaySituation = strategizerGatewayPhase.situations[k] + let situation = { + name: strategizerGatewaySituation.name, + conditions: [] + } + + for (let m = 0; m < strategizerGatewaySituation.conditions.length; m++) { + let strategizerGatewayCondition = strategizerGatewaySituation.conditions[m] + let condition = { + name: strategizerGatewayCondition.name, + code: strategizerGatewayCondition.code + } + situation.conditions.push(condition) + } + phase.situations.push(situation) + } + strategy.buyOrder.phases.push(phase) + } + dataToSave.strategy.subStrategies.push(strategy) + } + + return dataToSave + } + + async function loadFromStrategyzer () { + try { + const accessToken = window.localStorage.getItem(LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY) + + let user = window.localStorage.getItem(LOGGED_IN_USER_LOCAL_STORAGE_KEY) + if (user === null) { + // if there is no user that means that we are logged off, which means this object can not be used. + return + } + + user = JSON.parse(user) + + const authId = user.authId + + let sessionToken + + const apolloClient = new Apollo.lib.ApolloClient({ + networkInterface: Apollo.lib.createNetworkInterface({ + uri: window.canvasApp.graphQL.masterAppApiUrl, + transportBatching: true + }), + connectToDevTools: true + }) + + /* Getting the FB that own the Strategies */ + + let teamtradingSystemSimulationFB = '' + let teams = window.localStorage.getItem('userTeams') + if (teams === null || teams === '' || teams === []) { + if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> no user teams found at local storage. Can not get Strategies without them. ') } + return + } + teams = JSON.parse(teams) + for (let i = 0; i < teams.length; i++) { + let team = teams[i] + for (let j = 0; j < team.fb.length; j++) { + let fb = team.fb[j] + if (fb.slug.indexOf('simulator') >= 0) { + teamtradingSystemSimulationFB = fb.slug + } + } + } + + if (teamtradingSystemSimulationFB === '') { + if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> no simulation FB found at user teams. ') } + return + } + /* Getting all Strategies */ + + const networkInterface = Apollo.lib.createNetworkInterface({ + uri: window.canvasApp.graphQL.masterAppApiUrl + }) + + networkInterface.use([{ + applyMiddleware (req, next) { + req.options.headers = { + authorization: `Bearer ${accessToken}` + } + next() + } + }]) + + const getStrategies = () => { + return new Promise((resolve, reject) => { + apolloClient.query({ + query: GRAPHQL_QUERY_GET_STRATEGIES, + variables: { fbSlug: teamtradingSystemSimulationFB} + }) + .then(response => { + window.localStorage.setItem('userStrategies', JSON.stringify(response.data.strategizer_StrategyByFb)) + thisObject.strategizerData = JSON.parse(JSON.stringify(response.data.strategizer_StrategyByFb)) + + resolve({ strategies: response.data.strategizer_StrategyByFb.subStrategies}) + }) + .catch(err => { + if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> ApolloClient error getting user strategies -> err = ' + err.stack) } + reject(err) + }) + }) + } + + // To avoid race conditions, add asynchronous fetches to array + let fetchDataPromises = [] + + fetchDataPromises.push(getStrategies()) + + // When all asynchronous fetches resolve, authenticate user or throw error. + await Promise.all(fetchDataPromises).then(result => { + + }, err => { + if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> GraphQL Fetch Error -> err = ' + err.stack) } + }) + } catch (err) { + if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> err = ' + err.stack) } + } + } + + function getContainer (point) { + let container + + return undefined + } + + function draw () { + + } +} diff --git a/StrategySpace/StrategyCollectionItem.js b/StrategySpace/StrategyCollectionItem.js deleted file mode 100644 index 2b1b8c920..000000000 --- a/StrategySpace/StrategyCollectionItem.js +++ /dev/null @@ -1,177 +0,0 @@ -function newStrategyCollectionItem () { - const MODULE_NAME = 'Strategy Collection Item' - let thisObject = { - icon: undefined, - strategySource: undefined, - strategy: undefined, - container: undefined, - draw: draw, - getContainer: getContainer, - initialize: initialize - } - - let isInitialized = false - - thisObject.container = newContainer() - thisObject.container.name = MODULE_NAME - thisObject.container.initialize() - thisObject.container.isClickeable = true - thisObject.container.isDraggeable = false - - let status = 'off' - - return thisObject - - function initialize (strategy) { - thisObject.strategySource = strategy - - const ITEM_WIDTH = SIDE_PANEL_WIDTH - 20 - const ITEM_HEIGHT = 80 - - thisObject.container.frame.width = ITEM_WIDTH - thisObject.container.frame.height = ITEM_HEIGHT - - const LEFT_MARGIN = (thisObject.container.parentContainer.frame.width - thisObject.container.frame.width) / 2 - const TOP_MARGIN = 100 - - thisObject.container.frame.position.x = LEFT_MARGIN - thisObject.container.frame.position.y = TOP_MARGIN - - thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick) - isInitialized = true - } - - function onMouseClick (event) { - if (status === 'off') { - canvas.floatingSpace.makeVisible() - thisObject.strategy = newTradingSystem() - thisObject.strategy.initialize(thisObject.strategySource) - status = 'on' - } else { - canvas.floatingSpace.makeInvisible() - thisObject.strategy.finalize() - status = 'off' - } - } - - function getContainer (point) { - let container - if (thisObject.container.frame.isThisPointHere(point, true) === true) { - return thisObject.container - } else { - return undefined - } - } - - function draw () { - if (isInitialized === false) { return } - - borders() - text() - arrow() - icon() - } - - function text () { - if (thisObject.strategySource === undefined) { - return - } - - const LEFT_MARGIN = 80 - const TOP_MARGIN = 45 - - let strategy = thisObject.strategySource - - let labelpoint = { - x: LEFT_MARGIN, - y: TOP_MARGIN - } - - labelpoint = thisObject.container.frame.frameThisPoint(labelpoint) - - let labelToPrint = strategy.name - labelToPrint = '' - labelToPrint = labelToPrint.substring(0, 30) - let opacity = 1 - let fontSize = 15 - - printLabel(labelToPrint, labelpoint.x, labelpoint.y, opacity, fontSize) - } - - function borders () { - let params = { - cornerRadius: 8, - lineWidth: 0.1, - opacity: 1, - container: thisObject.container, - borderColor: UI_COLOR.DARK, - backgroundColor: UI_COLOR.WHITE, - castShadow: true - } - - roundedCornersBackground(params) - } - - function arrow () { - const X_OFFSET = 400 - const Y_OFFSET = 40 - const HEIGHT = 12 - const WIDTH = 6 - const LINE_WIDTH = 2 - const OPACITY = 0.6 - - point1 = { - x: X_OFFSET, - y: Y_OFFSET - HEIGHT / 2 - } - - point2 = { - x: X_OFFSET + WIDTH, - y: Y_OFFSET - } - - point3 = { - x: X_OFFSET, - y: Y_OFFSET + HEIGHT / 2 - } - - /* Now the transformations. */ - - point1 = thisObject.container.frame.frameThisPoint(point1) - point2 = thisObject.container.frame.frameThisPoint(point2) - point3 = thisObject.container.frame.frameThisPoint(point3) - - browserCanvasContext.setLineDash([0, 0]) - - /* The Arrow */ - - browserCanvasContext.beginPath() - - browserCanvasContext.moveTo(point1.x, point1.y) - browserCanvasContext.lineTo(point2.x, point2.y) - browserCanvasContext.lineTo(point3.x, point3.y) - - browserCanvasContext.lineWidth = LINE_WIDTH - browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK + ', ' + OPACITY + ')' - browserCanvasContext.stroke() - } - - function icon () { - if (thisObject.icon === undefined) { return } - - const X_OFFSET = 45 - const Y_OFFSET = 40 - const IMAGE_SIZE = 40 - - point1 = { - x: X_OFFSET, - y: Y_OFFSET - } - - /* Now the transformations. */ - - point1 = thisObject.container.frame.frameThisPoint(point1) - - browserCanvasContext.drawImage(thisObject.icon, point1.x - IMAGE_SIZE / 2, point1.y - IMAGE_SIZE / 2, IMAGE_SIZE, IMAGE_SIZE) - } -} diff --git a/StrategySpace/TradingSystem.js b/StrategySpace/TradingSystem.js deleted file mode 100644 index 9cd76e864..000000000 --- a/StrategySpace/TradingSystem.js +++ /dev/null @@ -1,410 +0,0 @@ - -function newTradingSystem () { - const MODULE_NAME = 'Trading System' - - let thisObject = { - protocolData: undefined, - container: undefined, - getContainer: getContainer, - initialize: initialize, - finalize: finalize - } - - thisObject.container = newContainer() - thisObject.container.initialize(MODULE_NAME) - thisObject.container.isClickeable = false - thisObject.container.isDraggeable = false - thisObject.container.isWheelable = false - thisObject.container.detectMouseOver = false - thisObject.container.frame.radius = 0 - thisObject.container.frame.position.x = 0 - thisObject.container.frame.position.y = 0 - thisObject.container.frame.width = 0 - thisObject.container.frame.height = 0 - - return thisObject - - function finalize () { - destroyStrategyParts() - thisObject.protocolData = undefined - thisObject.container.finalize() - thisObject.container = undefined - } - - function initialize (protocolData) { - thisObject.protocolData = { - id: protocolData.id, - strategies: protocolData.subStrategies - } - generateStrategyParts() - } - - function getContainer (point) { - - } - - function createPart (partType, name, node, parentNode, chainParent, title) { - let payload = {} - if (chainParent === undefined) { - payload.targetPosition = { - x: canvas.floatingSpace.container.frame.width / 2, - y: canvas.floatingSpace.container.frame.height / 2 - } - } else { - payload.targetPosition = { - x: chainParent.payload.position.x, - y: chainParent.payload.position.y - } - } - - if (title !== undefined) { - payload.subTitle = title - } else { - payload.subTitle = partType - } - - payload.visible = true - payload.title = name - payload.node = node - payload.parentNode = parentNode - payload.chainParent = chainParent - payload.onMenuItemClick = onMenuItemClick - - if (node.id === undefined) { - node.id = newUniqueId() - } - node.payload = payload - node.type = partType - canvas.floatingSpace.strategyPartConstructor.createStrategyPart(payload) - } - - function destroyPart (node) { - canvas.floatingSpace.strategyPartConstructor.destroyStrategyPart(node.payload) - } - - function generateStrategyParts () { - let lastPhase - let tradingSystem = thisObject.protocolData - - createPart('Trading System', '', tradingSystem, undefined, undefined) - - for (m = 0; m < tradingSystem.strategies.length; m++) { - let strategy = tradingSystem.strategies[m] - createPart('Strategy', strategy.name, strategy, tradingSystem, tradingSystem) - - createPart('Strategy Entry Event', '', strategy.entryPoint, strategy, strategy) - for (let k = 0; k < strategy.entryPoint.situations.length; k++) { - let situation = strategy.entryPoint.situations[k] - createPart('Situation', situation.name, situation, strategy.entryPoint, strategy.entryPoint, 'Strategy Entry' + ' ' + 'Situation') - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - createPart('Condition', condition.name, condition, situation, situation, 'Condition') - } - } - - createPart('Strategy Exit Event', '', strategy.exitPoint, strategy, strategy) - for (let k = 0; k < strategy.exitPoint.situations.length; k++) { - let situation = strategy.exitPoint.situations[k] - createPart('Situation', situation.name, situation, strategy.exitPoint, strategy.exitPoint, 'Strategy Exit' + ' ' + 'Situation') - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - createPart('Condition', condition.name, condition, situation, situation, 'Condition') - } - } - - createPart('Trade Entry Event', '', strategy.sellPoint, strategy, strategy) - for (let k = 0; k < strategy.sellPoint.situations.length; k++) { - let situation = strategy.sellPoint.situations[k] - createPart('Situation', situation.name, situation, strategy.sellPoint, strategy.sellPoint, 'Trade Entry' + ' ' + 'Situation') - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - createPart('Condition', condition.name, condition, situation, situation, 'Condition') - } - } - - createPart('Stop', '', strategy.stopLoss, strategy, strategy) - for (let p = 0; p < strategy.stopLoss.phases.length; p++) { - let phase = strategy.stopLoss.phases[p] - - let chainParent - if (p === 0) { - chainParent = strategy.stopLoss - } else { - chainParent = lastPhase - } - lastPhase = phase - createPart('Phase', phase.name, phase, strategy.stopLoss, chainParent, 'Stop Phase') - - for (let k = 0; k < phase.situations.length; k++) { - let situation = phase.situations[k] - createPart('Situation', situation.name, situation, phase, phase, 'Situation') - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - createPart('Condition', condition.name, condition, situation, situation, 'Condition') - } - } - } - - createPart('Take Profit', '', strategy.buyOrder, strategy, strategy) - for (let p = 0; p < strategy.buyOrder.phases.length; p++) { - let phase = strategy.buyOrder.phases[p] - let chainParent - if (p === 0) { - chainParent = strategy.buyOrder - } else { - chainParent = lastPhase - } - lastPhase = phase - createPart('Phase', phase.name, phase, strategy.buyOrder, chainParent, 'Take Profit Phase') - - for (let k = 0; k < phase.situations.length; k++) { - let situation = phase.situations[k] - createPart('Situation', situation.name, situation, phase, phase, 'Situation') - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - createPart('Condition', condition.name, condition, situation, situation, 'Condition') - } - } - } - } - } - - function destroyStrategyParts () { - let strategy = thisObject.protocolData - destroyPart(strategy) - - destroyPart(strategy.entryPoint) - for (let k = 0; k < strategy.entryPoint.situations.length; k++) { - let situation = strategy.entryPoint.situations[k] - destroyPart(situation) - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - destroyPart(condition) - } - } - - destroyPart(strategy.exitPoint) - for (let k = 0; k < strategy.exitPoint.situations.length; k++) { - let situation = strategy.exitPoint.situations[k] - destroyPart(situation) - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - destroyPart(condition) - } - } - - destroyPart(strategy.sellPoint) - for (let k = 0; k < strategy.sellPoint.situations.length; k++) { - let situation = strategy.sellPoint.situations[k] - destroyPart(situation) - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - destroyPart(condition) - } - } - - destroyPart(strategy.stopLoss) - for (let p = 0; p < strategy.stopLoss.phases.length; p++) { - let phase = strategy.stopLoss.phases[p] - destroyPart(strategy.stopLoss) - - for (let k = 0; k < phase.situations.length; k++) { - let situation = phase.situations[k] - destroyPart(situation) - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - destroyPart(condition) - } - } - } - - destroyPart(strategy.buyOrder) - for (let p = 0; p < strategy.buyOrder.phases.length; p++) { - let phase = strategy.buyOrder.phases[p] - destroyPart(strategy.buyOrder) - - for (let k = 0; k < phase.situations.length; k++) { - let situation = phase.situations[k] - destroyPart(situation) - - for (let m = 0; m < situation.conditions.length; m++) { - let condition = situation.conditions[m] - destroyPart(condition) - } - } - } - } - - async function onMenuItemClick (payload, action) { - switch (action) { - case 'Save Trading System': - { - let result = await canvas.strategySpace.workplace.saveToStrategyzer() - return result - break - } - - case 'Open Settings': - break - case 'Delete Strategy': - - break - - case 'Reload Strategy': - - break - - case 'Save Strategy': - - break - - case 'Edit Code': - - break - case 'Add Phase': - { - let phaseParent = payload.node - let m = phaseParent.phases.length - let phase = { - name: 'New Phase', - code: '', - situations: [] - } - phaseParent.phases.push(phase) - let phaseChainParent - if (m > 0) { - phaseChainParent = phaseParent.phases[m - 1] - } else { - phaseChainParent = phaseParent - } - createPart('Phase', phase.name, phase, phaseParent, phaseChainParent, 'Phase') - } - break - case 'Add Situation': - { - let phase = payload.node - let m = phase.situations.length - let situation = { - name: 'New Situation', - conditions: [] - } - phase.situations.push(situation) - createPart('Situation', situation.name, situation, phase, phase, 'Situation') - } - break - case 'Add Condition': - { - let situation = payload.node - let m = situation.conditions.length - let condition = { - name: 'New Condition', - code: '' - } - situation.conditions.push(condition) - createPart('Condition', condition.name, condition, situation, situation, 'Condition') - } - break - case 'Delete Phase': { - deletePhase(payload.node) - break - } - case 'Delete Situation': { - deleteSituation(payload.node) - break - } - case 'Delete Condition': { - deleteCondition(payload.node) - break - } - default: - - } - } - - function deletePhase (node) { - let payload = node.payload - for (let k = 0; k < payload.parentNode.phases.length; k++) { - let phase = payload.parentNode.phases[k] - if (phase.id === node.id) { - for (let j = 0; j < phase.situations.length; j++) { - let situation = phase.situations[j] - for (let i = 0; i < situation.conditions.length; i++) { - let condition = situation.conditions[i] - destroyPart(condition) - cleanNode(condition) - } - situation.conditions = [] - destroyPart(situation) - phase.situations.splice(j, 1) - cleanNode(situation) - } - phase.situations = [] - /* Before deleting this phase we need to give its chainParent to the next phase down the chain */ - if (k < payload.parentNode.phases.length - 1) { - payload.parentNode.phases[k + 1].payload.chainParent = payload.chainParent - } - /* Continue destroying this phase */ - destroyPart(phase) - payload.parentNode.phases.splice(k, 1) - cleanNode(phase) - return - } - } - } - - function deleteSituation (node) { - let payload = node.payload - for (let j = 0; j < payload.parentNode.situations.length; j++) { - let situation = payload.parentNode.situations[j] - if (situation.id === node.id) { - for (let i = 0; i < situation.conditions.length; i++) { - let condition = situation.conditions[i] - destroyPart(condition) - cleanNode(condition) - } - situation.conditions = [] - destroyPart(situation) - payload.parentNode.situations.splice(j, 1) - cleanNode(situation) - return - } - } - } - - function deleteCondition (node) { - let payload = node.payload - for (let i = 0; i < payload.parentNode.conditions.length; i++) { - let condition = payload.parentNode.conditions[i] - if (condition.id === node.id) { - destroyPart(node) - payload.parentNode.conditions.splice(i, 1) - cleanNode(condition) - return - } - } - } - - function cleanNode (node) { - node.payload.targetPosition.x = undefined - node.payload.targetPosition.y = undefined - node.payload.visible = undefined - node.payload.subTitle = undefined - node.payload.title = undefined - node.payload.node = undefined - node.payload.parentNode = undefined - node.payload.chainParent = undefined - node.payload.onMenuItemClick = undefined - node.handle = undefined - node.payload = undefined - node.cleaned = true - } -} diff --git a/StrategySpace/Workspace.js b/StrategySpace/Workspace.js index d533d8c14..f2c76368a 100644 --- a/StrategySpace/Workspace.js +++ b/StrategySpace/Workspace.js @@ -1,359 +1,875 @@ function newWorkspace () { const MODULE_NAME = 'Workspace' - const ERROR_LOG = true - const logger = newWebDebugLog() - logger.fileName = MODULE_NAME let thisObject = { - isDeployed: false, - strategizerData: undefined, - container: undefined, tradingSystem: undefined, - loadFromStrategyzer: loadFromStrategyzer, - saveToStrategyzer: saveToStrategyzer, - deploydTradingSystem: deploydTradingSystem, - draw: draw, + container: undefined, + spawn: spawn, + detachNode: detachNode, + attachNode: attachNode, getContainer: getContainer, - initialize: initialize + initialize: initialize, + finalize: finalize } - let colletionItems = [] - thisObject.container = newContainer() - thisObject.container.name = MODULE_NAME - thisObject.container.initialize() - + thisObject.container.initialize(MODULE_NAME) + thisObject.container.isClickeable = false + thisObject.container.isDraggeable = false + thisObject.container.isWheelable = false + thisObject.container.detectMouseOver = false + thisObject.container.frame.radius = 0 + thisObject.container.frame.position.x = 0 + thisObject.container.frame.position.y = 0 thisObject.container.frame.width = 0 thisObject.container.frame.height = 0 - thisObject.container.frame.position.x = 0 - thisObject.container.frame.position.y = 0 - - thisObject.container.isDraggeable = false - thisObject.container.isClickeable = false + spawnPosition = { + x: canvas.floatingSpace.container.frame.width / 2, + y: canvas.floatingSpace.container.frame.height / 2 + } return thisObject - function initialize () { - + function finalize () { + thisObject.tradingSystem = undefined + thisObject.container.finalize() + thisObject.container = undefined } - async function saveToStrategyzer () { - try { - const accessToken = window.localStorage.getItem(LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY) - - let user = window.localStorage.getItem(LOGGED_IN_USER_LOCAL_STORAGE_KEY) - if (user === null) { - if (ERROR_LOG === true) { logger.write('[ERROR] saveToStrategyzer -> Can not save because user is not logged in. ') } - return - } - - user = JSON.parse(user) - - const authId = user.authId - - const apolloClient = new Apollo.lib.ApolloClient({ - networkInterface: Apollo.lib.createNetworkInterface({ - uri: window.canvasApp.graphQL.masterAppApiUrl, - transportBatching: true - }), - connectToDevTools: true - }) - - /* Updating all Strategies */ - - const networkInterface = Apollo.lib.createNetworkInterface({ - uri: window.canvasApp.graphQL.masterAppApiUrl - }) - - networkInterface.use([{ - applyMiddleware (req, next) { - req.options.headers = { - authorization: `Bearer ${accessToken}` - } - next() - } - }]) - - let dataToSave = buildGraphQLDataToSave() - - const updateStrategies = () => { - return new Promise((resolve, reject) => { - apolloClient.mutate({ - mutation: GRAPHQL_MUTATION_UPDATE_STRATEGIES, - variables: { - id: thisObject.strategizerData.id, - strategy: dataToSave.strategy - } - }) - .then(response => { - resolve(true) - }) - .catch(err => { - if (ERROR_LOG === true) { logger.write('[ERROR] saveToStrategyzer -> ApolloClient error updating user strategies -> err = ' + err.stack) } - reject(err) - }) - }) - } - - let result = await updateStrategies() - - return result - } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] saveToStrategyzer -> err = ' + err.stack) } + function initialize (tradingSystem) { + thisObject.tradingSystem = { + id: tradingSystem.id, + strategies: tradingSystem.subStrategies } - } - - function buildGraphQLDataToSave () { - let graphQL_Mutation = '' - - let dataToSave = { - strategy: { - subStrategies: [] - } - } - - for (m = 0; m < thisObject.tradingSystem.protocolData.strategies.length; m++) { - let workspaceStrategy = thisObject.tradingSystem.protocolData.strategies[m] - let strategy = { - name: workspaceStrategy.name, - active: workspaceStrategy.active, - entryPoint: { - situations: [] - }, - exitPoint: { - situations: [] - }, - sellPoint: { - situations: [] - }, - stopLoss: { - phases: [] - }, - buyOrder: { - phases: [] - } - } - - for (let k = 0; k < workspaceStrategy.entryPoint.situations.length; k++) { - let workspaceSituation = workspaceStrategy.entryPoint.situations[k] - let situation = { - name: workspaceSituation.name, - conditions: [] - } - - for (let m = 0; m < workspaceSituation.conditions.length; m++) { - let workspaceCondition = workspaceSituation.conditions[m] - let condition = { - name: workspaceCondition.name, - code: workspaceCondition.code - } - situation.conditions.push(condition) - } - strategy.entryPoint.situations.push(situation) - } - - for (let k = 0; k < workspaceStrategy.exitPoint.situations.length; k++) { - let workspaceSituation = workspaceStrategy.exitPoint.situations[k] - let situation = { - name: workspaceSituation.name, - conditions: [] - } - - for (let m = 0; m < workspaceSituation.conditions.length; m++) { - let workspaceCondition = workspaceSituation.conditions[m] - let condition = { - name: workspaceCondition.name, - code: workspaceCondition.code - } - situation.conditions.push(condition) - } - strategy.exitPoint.situations.push(situation) - } - - for (let k = 0; k < workspaceStrategy.sellPoint.situations.length; k++) { - let workspaceSituation = workspaceStrategy.sellPoint.situations[k] - let situation = { - name: workspaceSituation.name, - conditions: [] - } - - for (let m = 0; m < workspaceSituation.conditions.length; m++) { - let workspaceCondition = workspaceSituation.conditions[m] - let condition = { - name: workspaceCondition.name, - code: workspaceCondition.code - } - situation.conditions.push(condition) - } - strategy.sellPoint.situations.push(situation) - } - - for (let p = 0; p < workspaceStrategy.stopLoss.phases.length; p++) { - let workspacePhase = workspaceStrategy.stopLoss.phases[p] - let phase = { - name: workspacePhase.name, - code: workspacePhase.code, - situations: [] - } - - for (let k = 0; k < workspacePhase.situations.length; k++) { - let workspaceSituation = workspacePhase.situations[k] - let situation = { - name: workspaceSituation.name, - conditions: [] - } - - for (let m = 0; m < workspaceSituation.conditions.length; m++) { - let workspaceCondition = workspaceSituation.conditions[m] - let condition = { - name: workspaceCondition.name, - code: workspaceCondition.code - } - situation.conditions.push(condition) - } - phase.situations.push(situation) - } - strategy.stopLoss.phases.push(phase) - } - - for (let p = 0; p < workspaceStrategy.buyOrder.phases.length; p++) { - let workspacePhase = workspaceStrategy.buyOrder.phases[p] - let phase = { - name: workspacePhase.name, - code: workspacePhase.code, - situations: [] - } - - for (let k = 0; k < workspacePhase.situations.length; k++) { - let workspaceSituation = workspacePhase.situations[k] - let situation = { - name: workspaceSituation.name, - conditions: [] - } - - for (let m = 0; m < workspaceSituation.conditions.length; m++) { - let workspaceCondition = workspaceSituation.conditions[m] - let condition = { - name: workspaceCondition.name, - code: workspaceCondition.code - } - situation.conditions.push(condition) - } - phase.situations.push(situation) - } - strategy.buyOrder.phases.push(phase) - } - dataToSave.strategy.subStrategies.push(strategy) - } - - return dataToSave - } - - async function loadFromStrategyzer () { - try { - const accessToken = window.localStorage.getItem(LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY) - - let user = window.localStorage.getItem(LOGGED_IN_USER_LOCAL_STORAGE_KEY) - if (user === null) { - // if there is no user that means that we are logged off, which means this object can not be used. - return - } - - user = JSON.parse(user) - - const authId = user.authId - - let sessionToken - - const apolloClient = new Apollo.lib.ApolloClient({ - networkInterface: Apollo.lib.createNetworkInterface({ - uri: window.canvasApp.graphQL.masterAppApiUrl, - transportBatching: true - }), - connectToDevTools: true - }) - - /* Getting the FB that own the Strategies */ - - let tradingSystemSimulationClone = '' - let clones = window.localStorage.getItem('userClones') - if (clones === null || clones === '') { - if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> no user clones found at local storage. Can not get Strategies without them. ') } - return - } - clones = JSON.parse(clones) - for (let i = 0; i < clones.length; i++) { - let clone = clones[i] - if (clone.botType === 'Indicator') { - tradingSystemSimulationClone = clone.botSlug - } - } - - /* Getting all Strategies */ - - const networkInterface = Apollo.lib.createNetworkInterface({ - uri: window.canvasApp.graphQL.masterAppApiUrl - }) - - networkInterface.use([{ - applyMiddleware (req, next) { - req.options.headers = { - authorization: `Bearer ${accessToken}` - } - next() - } - }]) - - const getStrategies = () => { - return new Promise((resolve, reject) => { - apolloClient.query({ - query: GRAPHQL_QUERY_GET_STRATEGIES, - variables: { fbSlug: tradingSystemSimulationClone} - }) - .then(response => { - window.localStorage.setItem('userStrategies', JSON.stringify(response.data.strategizer_StrategyByFb)) - thisObject.strategizerData = JSON.parse(JSON.stringify(response.data.strategizer_StrategyByFb)) - - resolve({ strategies: response.data.strategizer_StrategyByFb.subStrategies}) - }) - .catch(err => { - if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> ApolloClient error getting user strategies -> err = ' + err.stack) } - reject(err) - }) - }) - } - - // To avoid race conditions, add asynchronous fetches to array - let fetchDataPromises = [] - - fetchDataPromises.push(getStrategies()) - - // When all asynchronous fetches resolve, authenticate user or throw error. - await Promise.all(fetchDataPromises).then(result => { - - }, err => { - if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> GraphQL Fetch Error -> err = ' + err.stack) } - }) - } catch (err) { - if (ERROR_LOG === true) { logger.write('[ERROR] loadFromStrategyzer -> err = ' + err.stack) } - } - } - - function deploydTradingSystem () { - thisObject.tradingSystem = newTradingSystem() - thisObject.tradingSystem.initialize(thisObject.strategizerData) - thisObject.isDeployed = true + generateStrategyParts() } function getContainer (point) { - let container - return undefined } - function draw () { + function spawn (nodeText, point) { + point = canvas.floatingSpace.container.frame.unframeThisPoint(point) + spawnPosition.x = point.x + spawnPosition.y = point.y + let dirtyNode = JSON.parse(nodeText) + let rootNode = getProtocolNode(dirtyNode) + createPartFromNode(rootNode, undefined, undefined) + } + + function createPartFromNode (node, parentNode, chainParent) { + switch (node.type) { + case 'Condition': + { + let condition = node + createPart('Condition', condition.name, condition, parentNode, chainParent, 'Condition') + return + } + case 'Situation': { + let situation = node + createPart('Situation', situation.name, situation, parentNode, chainParent, 'Situation') + for (let m = 0; m < node.conditions.length; m++) { + let condition = node.conditions[m] + createPartFromNode(condition, situation, situation) + } + return + } + case 'Phase': { + let phase = node + createPart('Phase', phase.name, phase, parentNode, chainParent, phase.subType) + for (let m = 0; m < node.situations.length; m++) { + let situation = node.situations[m] + createPartFromNode(situation, phase, phase) + } + return + } + case 'Stop': { + let lastPhase + let stop = node + createPart('Stop', stop.name, stop, parentNode, chainParent, 'Stop') + for (let m = 0; m < node.phases.length; m++) { + let phase = node.phases[m] + let thisChainParent + if (m === 0) { + thisChainParent = node + } else { + thisChainParent = lastPhase + } + lastPhase = phase + createPartFromNode(phase, stop, thisChainParent) + } + return + } + case 'Take Profit': { + let lastPhase + let takeProfit = node + createPart('Take Profit', takeProfit.name, takeProfit, parentNode, chainParent, 'Take Profit') + for (let m = 0; m < node.phases.length; m++) { + let phase = node.phases[m] + let thisChainParent + if (m === 0) { + thisChainParent = node + } else { + thisChainParent = lastPhase + } + lastPhase = phase + createPartFromNode(phase, takeProfit, thisChainParent) + } + return + } + case 'Take Position Event': { + let event = node + createPart('Take Position Event', event.name, event, parentNode, chainParent, 'Take Position Event') + for (let m = 0; m < node.situations.length; m++) { + let situation = node.situations[m] + createPartFromNode(situation, event, event) + } + return + } + case 'Trigger On Event': { + let event = node + createPart('Trigger On Event', event.name, event, parentNode, chainParent, 'Trigger On Event') + for (let m = 0; m < node.situations.length; m++) { + let situation = node.situations[m] + createPartFromNode(situation, event, event) + } + return + } + case 'Trigger Off Event': { + let event = node + createPart('Trigger Off Event', event.name, event, parentNode, chainParent, 'Trigger Off Event') + for (let m = 0; m < node.situations.length; m++) { + let situation = node.situations[m] + createPartFromNode(situation, event, event) + } + return + } + case 'Strategy': { + let strategy = node + createPart('Strategy', strategy.name, strategy, parentNode, chainParent, 'Strategy') + createPartFromNode(node.entryPoint, strategy, strategy) + createPartFromNode(node.exitPoint, strategy, strategy) + createPartFromNode(node.sellPoint, strategy, strategy) + createPartFromNode(node.stopLoss, strategy, strategy) + createPartFromNode(node.buyOrder, strategy, strategy) + return + } + case 'Trading System': { + let tradingSystem = node + createPart('Trading System', tradingSystem.name, tradingSystem, parentNode, chainParent, 'Trading System') + for (let m = 0; m < node.strategies.length; m++) { + let strategy = node.strategies[m] + createPartFromNode(strategy, tradingSystem, tradingSystem) + } + return + } + } + } + + function detachNode (node) { + switch (node.type) { + case 'Trading System': { + return + } + case 'Strategy': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.strategies.length; i++) { + let strategy = payload.parentNode.strategies[i] + if (strategy.id === node.id) { + payload.parentNode.strategies.splice(i, 1) + } + payload.parentNode = undefined + payload.chainParent = undefined + } + } + break + case 'Trigger On Event': { + return + } + break + case 'Trigger Off Event': { + return + } + break + case 'Take Position Event': { + return + } + break + case 'Stop': { + return + } + break + case 'Take Profit': { + return + } + break + case 'Condition': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.conditions.length; i++) { + let condition = payload.parentNode.conditions[i] + if (condition.id === node.id) { + payload.parentNode.conditions.splice(i, 1) + } + } + node.payload.chainParent = undefined + node.payload.parentNode = undefined + } + break + case 'Situation': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.situations.length; i++) { + let situation = payload.parentNode.situations[i] + if (situation.id === node.id) { + payload.parentNode.situations.splice(i, 1) + } + } + node.payload.chainParent = undefined + node.payload.parentNode = undefined + } + break + case 'Phase': { + let payload = node.payload + for (let i = 0; i < payload.parentNode.phases.length; i++) { + let phase = payload.parentNode.phases[i] + if (phase.id === node.id) { + if (i < payload.parentNode.phases.length - 1) { + let nextPhase = payload.parentNode.phases[i + 1] + if (i > 0) { + let previousPhase = payload.parentNode.phases[i - 1] + nextPhase.payload.chainParent = previousPhase + } else { + nextPhase.payload.chainParent = payload.parentNode + } + } + payload.parentNode.phases.splice(i, 1) + payload.parentNode = undefined + payload.chainParent = undefined + return + } + } + } + } + } + + function attachNode (node, attachToNode) { + switch (node.type) { + case 'Strategy': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.strategies.push(node) + } + break + case 'Phase': { + switch (attachToNode.type) { + case 'Stop': { + node.payload.parentNode = attachToNode + if (attachToNode.phases.length > 0) { + let phase = attachToNode.phases[attachToNode.phases.length - 1] + node.payload.chainParent = phase + } else { + node.payload.chainParent = attachToNode + } + attachToNode.phases.push(node) + } + break + case 'Take Profit': { + node.payload.parentNode = attachToNode + if (attachToNode.phases.length > 0) { + let phase = attachToNode.phases[attachToNode.phases.length - 1] + node.payload.chainParent = phase + } else { + node.payload.chainParent = attachToNode + } + attachToNode.phases.push(node) + } + break + case 'Phase': { + node.payload.parentNode = attachToNode.payload.parentNode + for (let i = 0; i < node.payload.parentNode.phases.length; i++) { + let phase = node.payload.parentNode.phases[i] + if (attachToNode.id === phase.id) { + if (i === node.payload.parentNode.phases.length - 1) { + node.payload.chainParent = attachToNode + node.payload.parentNode.phases.push(node) + } else { + node.payload.chainParent = attachToNode + let nextPhase = node.payload.parentNode.phases[i + 1] + nextPhase.payload.chainParent = node + node.payload.parentNode.phases.splice(i + 1, 0, node) + return + } + } + } + } + } + } + break + case 'Situation': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.situations.push(node) + } + break + case 'Condition': { + node.payload.parentNode = attachToNode + node.payload.chainParent = attachToNode + node.payload.parentNode.conditions.push(node) + } + break + } + } + + function createPart (partType, name, node, parentNode, chainParent, title) { + let payload = {} + if (chainParent === undefined) { + payload.targetPosition = { + x: spawnPosition.x, + y: spawnPosition.y + } + } else { + payload.targetPosition = { + x: chainParent.payload.position.x, + y: chainParent.payload.position.y + } + } + + if (title !== undefined) { + payload.subTitle = title + } else { + payload.subTitle = partType + } + + payload.visible = true + payload.title = name + payload.node = node + payload.parentNode = parentNode + payload.chainParent = chainParent + payload.onMenuItemClick = onMenuItemClick + + if (node.id === undefined) { + node.id = newUniqueId() + } + node.payload = payload + node.type = partType + canvas.floatingSpace.strategyPartConstructor.createStrategyPart(payload) + } + + function destroyPart (node) { + canvas.floatingSpace.strategyPartConstructor.destroyStrategyPart(node.payload) + } + + function generateStrategyParts () { + let lastPhase + let tradingSystem = thisObject.tradingSystem + + createPart('Trading System', '', tradingSystem, undefined, undefined) + + for (m = 0; m < tradingSystem.strategies.length; m++) { + let strategy = tradingSystem.strategies[m] + createPart('Strategy', strategy.name, strategy, tradingSystem, tradingSystem) + + createPart('Trigger On Event', '', strategy.entryPoint, strategy, strategy) + for (let k = 0; k < strategy.entryPoint.situations.length; k++) { + let situation = strategy.entryPoint.situations[k] + createPart('Situation', situation.name, situation, strategy.entryPoint, strategy.entryPoint, 'Trigger On' + ' ' + 'Situation') + + for (let m = 0; m < situation.conditions.length; m++) { + let condition = situation.conditions[m] + createPart('Condition', condition.name, condition, situation, situation, 'Condition') + } + } + + createPart('Trigger Off Event', '', strategy.exitPoint, strategy, strategy) + for (let k = 0; k < strategy.exitPoint.situations.length; k++) { + let situation = strategy.exitPoint.situations[k] + createPart('Situation', situation.name, situation, strategy.exitPoint, strategy.exitPoint, 'Trigger Off' + ' ' + 'Situation') + + for (let m = 0; m < situation.conditions.length; m++) { + let condition = situation.conditions[m] + createPart('Condition', condition.name, condition, situation, situation, 'Condition') + } + } + + createPart('Take Position Event', '', strategy.sellPoint, strategy, strategy) + for (let k = 0; k < strategy.sellPoint.situations.length; k++) { + let situation = strategy.sellPoint.situations[k] + createPart('Situation', situation.name, situation, strategy.sellPoint, strategy.sellPoint, 'Take Position' + ' ' + 'Situation') + + for (let m = 0; m < situation.conditions.length; m++) { + let condition = situation.conditions[m] + createPart('Condition', condition.name, condition, situation, situation, 'Condition') + } + } + + createPart('Stop', '', strategy.stopLoss, strategy, strategy) + for (let p = 0; p < strategy.stopLoss.phases.length; p++) { + let phase = strategy.stopLoss.phases[p] + + let chainParent + if (p === 0) { + chainParent = strategy.stopLoss + } else { + chainParent = lastPhase + } + lastPhase = phase + createPart('Phase', phase.name, phase, strategy.stopLoss, chainParent, 'Stop Phase') + + for (let k = 0; k < phase.situations.length; k++) { + let situation = phase.situations[k] + createPart('Situation', situation.name, situation, phase, phase, 'Situation') + + for (let m = 0; m < situation.conditions.length; m++) { + let condition = situation.conditions[m] + createPart('Condition', condition.name, condition, situation, situation, 'Condition') + } + } + } + + createPart('Take Profit', '', strategy.buyOrder, strategy, strategy) + for (let p = 0; p < strategy.buyOrder.phases.length; p++) { + let phase = strategy.buyOrder.phases[p] + let chainParent + if (p === 0) { + chainParent = strategy.buyOrder + } else { + chainParent = lastPhase + } + lastPhase = phase + createPart('Phase', phase.name, phase, strategy.buyOrder, chainParent, 'Take Profit Phase') + + for (let k = 0; k < phase.situations.length; k++) { + let situation = phase.situations[k] + createPart('Situation', situation.name, situation, phase, phase, 'Situation') + + for (let m = 0; m < situation.conditions.length; m++) { + let condition = situation.conditions[m] + createPart('Condition', condition.name, condition, situation, situation, 'Condition') + } + } + } + } + } + + async function onMenuItemClick (payload, action) { + switch (action) { + case 'Save Trading System': + { + let result = await canvas.strategySpace.strategizerGateway.saveToStrategyzer(canvas.strategySpace.workspace.tradingSystem) + return result + break + } + + case 'Edit Code': + + break + case 'Download': + + let text = JSON.stringify(getProtocolNode(payload.node)) + let nodeName = payload.node.name + if (nodeName === undefined) { + nodeName = '' + } else { + nodeName = '.' + nodeName + } + let fileName = payload.node.type + nodeName + '.json' + download(fileName, text) + + break + + case 'New Strategy': + { + let strategyParent = payload.node + + let strategy = { + name: 'New Strategy', + active: true, + entryPoint: { + situations: [] + }, + exitPoint: { + situations: [] + }, + sellPoint: { + situations: [] + }, + stopLoss: { + phases: [] + }, + buyOrder: { + phases: [] + } + } + + strategyParent.strategies.push(strategy) + createPart('Strategy', strategy.name, strategy, strategyParent, strategyParent, 'Strategy') + createPart('Trigger On Event', '', strategy.entryPoint, strategy, strategy) + createPart('Trigger Off Event', '', strategy.exitPoint, strategy, strategy) + createPart('Take Position Event', '', strategy.sellPoint, strategy, strategy) + createPart('Stop', '', strategy.stopLoss, strategy, strategy) + createPart('Take Profit', '', strategy.buyOrder, strategy, strategy) + } + break + case 'Add Phase': + { + let phaseParent = payload.node + let m = phaseParent.phases.length + let phase = { + name: 'New Phase', + code: '', + situations: [] + } + phaseParent.phases.push(phase) + let phaseChainParent + if (m > 0) { + phaseChainParent = phaseParent.phases[m - 1] + } else { + phaseChainParent = phaseParent + } + createPart('Phase', phase.name, phase, phaseParent, phaseChainParent, 'Phase') + } + break + case 'Add Situation': + { + let phase = payload.node + let m = phase.situations.length + let situation = { + name: 'New Situation', + conditions: [] + } + phase.situations.push(situation) + createPart('Situation', situation.name, situation, phase, phase, 'Situation') + } + break + case 'Add Condition': + { + let situation = payload.node + let m = situation.conditions.length + let condition = { + name: 'New Condition', + code: '' + } + situation.conditions.push(condition) + createPart('Condition', condition.name, condition, situation, situation, 'Condition') + } + break + case 'Delete Strategy': { + deleteStrategy(payload.node) + break + } + case 'Delete Phase': { + deletePhase(payload.node) + break + } + case 'Delete Situation': { + deleteSituation(payload.node) + break + } + case 'Delete Condition': { + deleteCondition(payload.node) + break + } + default: + + } + } + + function deleteStrategy (node) { + let payload = node.payload + if (payload.parentNode !== undefined) { + for (let j = 0; j < payload.parentNode.strategies.length; j++) { + let strategy = payload.parentNode.strategies[j] + if (strategy.id === node.id) { + deleteEvent(strategy.entryPoint) + deleteEvent(strategy.exitPoint) + deleteEvent(strategy.sellPoint) + deleteManagedItem(strategy.stopLoss) + deleteManagedItem(strategy.buyOrder) + payload.parentNode.strategies.splice(j, 1) + destroyPart(strategy) + cleanNode(strategy) + return + } + } + } else { + deleteEvent(node.entryPoint) + deleteEvent(node.exitPoint) + deleteEvent(node.sellPoint) + deleteManagedItem(node.stopLoss) + deleteManagedItem(node.buyOrder) + destroyPart(node) + cleanNode(node) + } + } + + function deleteEvent (node) { + while (node.situations.length > 0) { + deleteSituation(node.situations[0]) + } + destroyPart(node) + cleanNode(node) + } + + function deleteManagedItem (node) { + while (node.phases.length > 0) { + deletePhase(node.phases[0]) + } + destroyPart(node) + cleanNode(node) + } + + function deletePhase (node) { + let payload = node.payload + if (payload.parentNode !== undefined) { + for (let k = 0; k < payload.parentNode.phases.length; k++) { + let phase = payload.parentNode.phases[k] + if (phase.id === node.id) { + while (phase.situations.length > 0) { + let situation = phase.situations[0] + deleteSituation(situation) + } + phase.situations = [] + /* Before deleting this phase we need to give its chainParent to the next phase down the chain */ + if (k < payload.parentNode.phases.length - 1) { + payload.parentNode.phases[k + 1].payload.chainParent = payload.chainParent + } + /* Continue destroying this phase */ + payload.parentNode.phases.splice(k, 1) + destroyPart(phase) + cleanNode(phase) + return + } + } + } else { + while (node.situations.length > 0) { + let situation = node.situations[0] + deleteSituation(situation) + } + node.situations = [] + destroyPart(node) + cleanNode(node) + } + } + + function deleteSituation (node) { + let payload = node.payload + if (payload.parentNode !== undefined) { + for (let j = 0; j < payload.parentNode.situations.length; j++) { + let situation = payload.parentNode.situations[j] + if (situation.id === node.id) { + while (situation.conditions.length > 0) { + let condition = situation.conditions[0] + deleteCondition(condition) + } + situation.conditions = [] + payload.parentNode.situations.splice(j, 1) + destroyPart(situation) + cleanNode(situation) + return + } + } + } else { + while (node.conditions.length > 0) { + let condition = node.conditions[0] + deleteCondition(condition) + } + node.conditions = [] + destroyPart(node) + cleanNode(node) + } + } + + function deleteCondition (node) { + let payload = node.payload + if (payload.parentNode !== undefined) { + for (let i = 0; i < payload.parentNode.conditions.length; i++) { + let condition = payload.parentNode.conditions[i] + if (condition.id === node.id) { + payload.parentNode.conditions.splice(i, 1) + destroyPart(node) + cleanNode(condition) + return + } + } + } else { + destroyPart(node) + cleanNode(node) + } + } + + function cleanNode (node) { + node.payload.targetPosition.x = undefined + node.payload.targetPosition.y = undefined + node.payload.visible = undefined + node.payload.subTitle = undefined + node.payload.title = undefined + node.payload.node = undefined + node.payload.parentNode = undefined + node.payload.chainParent = undefined + node.payload.onMenuItemClick = undefined + node.handle = undefined + node.payload = undefined + node.cleaned = true + } + + function download (filename, text) { + let element = document.createElement('a') + element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text)) + element.setAttribute('download', filename) + element.style.display = 'none' + document.body.appendChild(element) + element.click() + document.body.removeChild(element) + } + + function getProtocolNode (node) { + switch (node.type) { + case 'Condition': + { + let condition = { + type: node.type, + name: node.name, + code: node.code + } + return condition + break + } + case 'Situation': { + let situation = { + type: node.type, + name: node.name, + conditions: [] + } + + for (let m = 0; m < node.conditions.length; m++) { + let condition = getProtocolNode(node.conditions[m]) + situation.conditions.push(condition) + } + return situation + break + } + case 'Phase': { + let phase = { + type: node.type, + subType: node.subType, + name: node.name, + code: node.code, + situations: [] + } + + for (let m = 0; m < node.situations.length; m++) { + let situation = getProtocolNode(node.situations[m]) + phase.situations.push(situation) + } + return phase + break + } + case 'Stop': { + let stop = { + type: node.type, + subType: node.subType, + name: node.name, + phases: [] + } + + for (let m = 0; m < node.phases.length; m++) { + let phase = getProtocolNode(node.phases[m]) + stop.phases.push(phase) + } + return stop + break + } + case 'Take Profit': { + let takeProfit = { + type: node.type, + subType: node.subType, + name: node.name, + phases: [] + } + + for (let m = 0; m < node.phases.length; m++) { + let phase = getProtocolNode(node.phases[m]) + takeProfit.phases.push(phase) + } + return takeProfit + break + } + case 'Take Position Event': { + let event = { + type: node.type, + subType: node.subType, + name: node.name, + situations: [] + } + + for (let m = 0; m < node.situations.length; m++) { + let situation = getProtocolNode(node.situations[m]) + event.situations.push(situation) + } + return event + break + } + case 'Trigger On Event': { + let event = { + type: node.type, + subType: node.subType, + name: node.name, + situations: [] + } + + for (let m = 0; m < node.situations.length; m++) { + let situation = getProtocolNode(node.situations[m]) + event.situations.push(situation) + } + return event + break + } + case 'Trigger Off Event': { + let event = { + type: node.type, + subType: node.subType, + name: node.name, + situations: [] + } + + for (let m = 0; m < node.situations.length; m++) { + let situation = getProtocolNode(node.situations[m]) + event.situations.push(situation) + } + return event + break + } + case 'Strategy': { + let strategy = { + type: node.type, + subType: node.subType, + name: node.name, + entryPoint: getProtocolNode(node.entryPoint), + exitPoint: getProtocolNode(node.exitPoint), + sellPoint: getProtocolNode(node.sellPoint), + stopLoss: getProtocolNode(node.stopLoss), + buyOrder: getProtocolNode(node.buyOrder) + } + return strategy + break + } + case 'Trading System': { + let tradingSystem = { + type: node.type, + subType: node.subType, + name: node.name, + strategies: [] + } + + for (let m = 0; m < node.strategies.length; m++) { + let strategy = getProtocolNode(node.strategies[m]) + tradingSystem.strategies.push(strategy) + } + return tradingSystem + break + } + } } }