3448 files changed
+566483
-263
No files matched your search
@@ -0,0 +1,38 @@
|
||||
|
||||
#PORT NUMBERS: If any of these ports are already in use by a different app, please pick a new number between 1 and 50000.
|
||||
WEB_SERVER_PORT=9999
|
||||
WEB_SOCKETS_PORT=8080
|
||||
|
||||
|
||||
# TASK MANAGER VARIABLES:
|
||||
|
||||
TASK_SERVER_PATH=./TaskServer
|
||||
|
||||
# TASK SERVER VARIABLES:
|
||||
|
||||
INTER_PROCESS_FILES_PATH=./Inter-Process-Files
|
||||
STORAGE_PATH=./Data-Storage
|
||||
PLOTTERS_PATH=./Legacy-Code
|
||||
|
||||
USE_KEYVAULT=false
|
||||
WRITE_LOGS_TO_FILES=true
|
||||
BOTS_PATH=./Legacy-Code
|
||||
LOG_PATH=./Log-Files
|
||||
|
||||
# WEB SERVER VARIABLES:
|
||||
|
||||
#Log options
|
||||
CONSOLE_LOG=false
|
||||
CONSOLE_ERROR_LOG=true
|
||||
LOG_FILE_CONTENT=false
|
||||
|
||||
# Server commands
|
||||
REINITIALIZE_COMMAND=sdnacGYGdw756dnxasxbGGGjsdbd5576576gfdsxgvbnFFF
|
||||
|
||||
PATH_TO_CANVAS_APP=./CanvasApp
|
||||
PATH_TO_FILES_COMPONENT=./Files-Component/src
|
||||
PATH_TO_WEB_SERVER=./WebServer/
|
||||
|
||||
GATEWAY_ENDPOINT=https://demo-api-dev.superalgos.org/graphql
|
||||
|
||||
|
||||
+6
-6
@@ -2,21 +2,21 @@
|
||||
|
||||
We are thrilled you are interested in contributing to this project. Your help may be essential to the project's success and we want you in!
|
||||
|
||||
Superalgos is a collective project, currently at a very early stage. We not only welcome contributions but may even choose to incentivize relevant contributions via the project's native crypto token.
|
||||
Superalgos is a collective project, currently at a very early stage. We not only welcome contributions but may even choose to incentivize relevant contributions via the project's native crypto token, to be deployed in the near future.
|
||||
|
||||
Please read the [Join the Dev Team]( https://superalgos.org/developers-join-dev-team.shtml) page in our website to learn more about how you may become a relevant member of the Superalgos Project and even make a living of contributing code.
|
||||
|
||||
Contributions are deemed _voluntary work_, irrespective of the incentives that may be in place, and are subject to the conditions described in the software [LICENSE](https://github.com/Superalgos/DesktopApp/blob/master/LICENSE).
|
||||
Contributions are deemed _voluntary work_, irrespective of the incentives that may be in place, and are subject to the conditions described in the software [LICENSE](LICENSE).
|
||||
|
||||
## Getting Started
|
||||
|
||||
First step is to download, install and start using the app. The release includes all source code which constitutes your development environment right out of the package. The app runs uncompilled on the actual code.
|
||||
First step is to fork this repository and start using the app. The repository includes all source code which constitutes your development environment. The app runs uncompilled on the actual code.
|
||||
|
||||
* The priority at this moment is the stabilization of existing functionality. Your first contribution will be using and testing the app, and reporting any bugs you may find.
|
||||
* The priority at this moment is the stabilization of existing functionality. Your first contribution will be using, learning, and testing the app, and reporting any bugs you may find.
|
||||
|
||||
* You may debug and remove bugs yourself, and submit pull requests to the corresponding repositories. The name of the folder containing the code maps each of the repositories involved in the release.
|
||||
* You may debug and remove bugs yourself, and submit pull requests.
|
||||
|
||||
* Additionally, you may contribute helping improve the README and building the documentation in each of the repositories involved.
|
||||
* Another top priority is building a robust base of indicators and technical studies. This is done from the UI, using the functionality managed by data mines.
|
||||
|
||||
As you engage with other developers and the Core Team, we will get to know you and develop a relationship, so that you may later participate in core development tasks.
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
|
||||
function newAnimation () {
|
||||
const MODULE_NAME = 'Animation'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
/*
|
||||
|
||||
Essentially, what the animation object does, is to call several functions in order to draw the content of each frame.
|
||||
To do that, it has an array of functions that need to be called, and it just goes one by one through them. His parent object
|
||||
is responsible for providing more functions to call, or removing functions from its list when they are not longer needed.
|
||||
|
||||
*/
|
||||
|
||||
let animationLoopHandle // This is the handle to the animation loop. With this handle we can cancel the loop, for instance.
|
||||
let callBackFunctions = new Map()
|
||||
|
||||
let thisObject = {
|
||||
start: start,
|
||||
stop: stop,
|
||||
addCallBackFunction: addCallBackFunction,
|
||||
removeCallBackFunction: removeCallBackFunction,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let totalConsumption = 0
|
||||
let totalCounter = 0
|
||||
let pointerIcon
|
||||
let videoRecorder
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.stop()
|
||||
pointerIcon = undefined
|
||||
videoRecorder = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
videoRecorder = newVideoRecorder()
|
||||
}
|
||||
|
||||
function start () {
|
||||
animationLoop() // Inside this function the animation process is started, and at the same time it creates a loop.
|
||||
}
|
||||
|
||||
function stop () {
|
||||
window.cancelAnimationFrame(animationLoopHandle)
|
||||
}
|
||||
|
||||
function addCallBackFunction (key, callBack) {
|
||||
callBackFunctions.set(key, callBack)
|
||||
}
|
||||
|
||||
function removeCallBackFunction (key) {
|
||||
callBackFunctions.delete(key)
|
||||
}
|
||||
|
||||
function animationLoop () {
|
||||
try {
|
||||
if (window.canvasApp.visible === true) {
|
||||
/* We set the canvas to its normal width and height */
|
||||
browserCanvas.width = window.innerWidth
|
||||
browserCanvas.height = window.innerHeight - CURRENT_TOP_MARGIN
|
||||
|
||||
/* First thing is to clear the actual canvas */
|
||||
// clearBrowserCanvas()
|
||||
|
||||
/* We loop through the callback functions collections and execute them all. */
|
||||
let performanceMap = new Map()
|
||||
let totalTimeConsumed = 0
|
||||
let totalElements = 0
|
||||
let chanceToExecute
|
||||
let randomNumber
|
||||
let mustExecute
|
||||
|
||||
// console.clear()
|
||||
let row = 0
|
||||
for (const [key, callBackFunction] of callBackFunctions.entries()) {
|
||||
row++
|
||||
switch (key) {
|
||||
case 'Floating Space Physics':
|
||||
chanceToExecute = 50
|
||||
randomNumber = Math.random() * 100 * chanceToExecute
|
||||
if (randomNumber > 100 - chanceToExecute) { mustExecute = true } else { mustExecute = false }
|
||||
break
|
||||
default: {
|
||||
mustExecute = true
|
||||
}
|
||||
|
||||
}
|
||||
let timeConsumed = 0
|
||||
if (mustExecute === true) {
|
||||
let t0 = performance.now()
|
||||
callBackFunction()
|
||||
let t1 = performance.now()
|
||||
timeConsumed = t1 - t0
|
||||
if (key === 'Charting Space Draw') {
|
||||
if (Math.random() * 100 > 99) {
|
||||
totalConsumption = 0
|
||||
totalCounter = 0
|
||||
}
|
||||
|
||||
totalConsumption = totalConsumption + timeConsumed
|
||||
totalCounter = totalCounter + 1
|
||||
}
|
||||
performanceMap.set(key, timeConsumed)
|
||||
totalTimeConsumed = totalTimeConsumed + timeConsumed
|
||||
totalElements++
|
||||
}
|
||||
}
|
||||
|
||||
/* Performance Check */
|
||||
if (SHOW_ANIMATION_PERFORMACE === true) {
|
||||
row = 0
|
||||
for (const [key, timeConsumed] of performanceMap.entries()) {
|
||||
row++
|
||||
labelToPrint = key + ' ' + timeConsumed.toFixed(4)
|
||||
printLabel(labelToPrint, 10, 100 + row * 30, 1, 20, UI_COLOR.RED)
|
||||
let percentage = timeConsumed * 100 / totalTimeConsumed
|
||||
labelToPrint = key + ' ' + percentage.toFixed(1) + '%'
|
||||
printLabel(labelToPrint, 300, 100 + row * 30, 1, 20, UI_COLOR.RED)
|
||||
}
|
||||
|
||||
/* Other Variables */
|
||||
row++
|
||||
printLabel(DEBUG.variable1, 300, 100 + row * 30, 1, 20, UI_COLOR.RED)
|
||||
row++
|
||||
printLabel(DEBUG.variable2, 300, 100 + row * 30, 1, 20, UI_COLOR.RED)
|
||||
row++
|
||||
printLabel(DEBUG.variable3, 300, 100 + row * 30, 1, 20, UI_COLOR.RED)
|
||||
row++
|
||||
printLabel(DEBUG.variable4, 300, 100 + row * 30, 1, 20, UI_COLOR.RED)
|
||||
}
|
||||
} else {
|
||||
browserCanvas.width = 1
|
||||
browserCanvas.height = 1
|
||||
}
|
||||
|
||||
/* Media Recording */
|
||||
if (areWeRecording === true) {
|
||||
videoRecorder.recordCanvas()
|
||||
}
|
||||
|
||||
/* We request the next frame to be drawn, and stablishing a loop */
|
||||
|
||||
/* Use this code for full animation speed. */
|
||||
animationLoopHandle = window.requestAnimationFrame(animationLoop)
|
||||
|
||||
/* Use this code for max 10 frames per second animation speed, if the app is consumming too much of your CPU. */
|
||||
/*
|
||||
setTimeout(nextLoop, 100)
|
||||
function nextLoop () {
|
||||
animationLoopHandle = window.requestAnimationFrame(animationLoop)
|
||||
}
|
||||
*/
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] animationLoop -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function clearBrowserCanvas () {
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
browserCanvasContext.rect(0, 0, browserCanvas.width, browserCanvas.height)
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,990 @@
|
||||
|
||||
let browserCanvasContext // The context of the canvas object.
|
||||
|
||||
let stepsInitializationCounter = 0 // This counter the initialization steps required to be able to turn off the splash screen.
|
||||
let marketInitializationCounter = 0 // This counter the initialization of markets required to be able to turn off the splash screen.
|
||||
let splashScreenNeeded = true
|
||||
|
||||
function newCanvas () {
|
||||
const MODULE_NAME = 'Canvas'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
/* Mouse event related variables. */
|
||||
|
||||
let containerDragStarted = false
|
||||
let floatingObjectDragStarted = false
|
||||
let floatingObjectBeingDragged
|
||||
let containerBeingDragged
|
||||
let viewPortBeingDragged = false
|
||||
let ignoreNextClick = false
|
||||
|
||||
let dragVector = {
|
||||
downX: 0,
|
||||
downY: 0,
|
||||
upX: 0,
|
||||
upY: 0
|
||||
}
|
||||
|
||||
/* canvas object */
|
||||
|
||||
let thisObject = {
|
||||
eventHandler: undefined,
|
||||
topSpace: undefined,
|
||||
chartingSpace: undefined,
|
||||
floatingSpace: undefined,
|
||||
panelsSpace: undefined,
|
||||
cockpitSpace: undefined,
|
||||
bottomSpace: undefined,
|
||||
designSpace: undefined,
|
||||
animation: undefined,
|
||||
mouse: undefined,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.eventHandler = newEventHandler()
|
||||
|
||||
let splashScreen
|
||||
let lastContainerMouseOver
|
||||
let lastShortcutKeyRejection
|
||||
|
||||
thisObject.mouse = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
action: ''
|
||||
}
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
try {
|
||||
thisObject.chartingSpace.finalize()
|
||||
thisObject.floatingSpace.finalize()
|
||||
|
||||
browserCanvas.removeEventListener('mousedown', onMouseDown, false)
|
||||
browserCanvas.removeEventListener('mouseup', onMouseUp, false)
|
||||
browserCanvas.removeEventListener('mousemove', onMouseMove, false)
|
||||
browserCanvas.removeEventListener('click', onMouseClick, false)
|
||||
browserCanvas.removeEventListener('mouseout', onMouseOut, false)
|
||||
|
||||
if (browserCanvas.removeEventListener) {
|
||||
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)
|
||||
|
||||
browserCanvas.removeEventListener('keydown', onKeyDown, false)
|
||||
browserCanvas.removeEventListener('keyup', onKeyUp, false)
|
||||
|
||||
splashScreen = undefined
|
||||
lastContainerMouseOver = undefined
|
||||
|
||||
hisObject.mouse = undefined
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
try {
|
||||
initializeBrowserCanvas()
|
||||
|
||||
addCanvasEvents()
|
||||
|
||||
/* Instantiate all the children spaces of Canvas object */
|
||||
|
||||
thisObject.floatingSpace = newFloatingSpace()
|
||||
thisObject.floatingSpace.initialize()
|
||||
|
||||
thisObject.topSpace = newTopSpace()
|
||||
thisObject.topSpace.initialize()
|
||||
|
||||
thisObject.designSpace = newDesignSpace()
|
||||
thisObject.designSpace.initialize()
|
||||
|
||||
thisObject.cockpitSpace = newCockpitSpace()
|
||||
thisObject.cockpitSpace.initialize()
|
||||
|
||||
thisObject.panelsSpace = newPanelsSpace()
|
||||
thisObject.panelsSpace.initialize()
|
||||
|
||||
thisObject.chartingSpace = newChartingSpace()
|
||||
thisObject.chartingSpace.initialize()
|
||||
|
||||
thisObject.bottomSpace = thisObject.cockpitSpace
|
||||
|
||||
splashScreen = newSplashScreen()
|
||||
splashScreen.initialize()
|
||||
|
||||
let animation = newAnimation()
|
||||
animation.initialize()
|
||||
|
||||
thisObject.animation = animation
|
||||
/* Low Level Infraestructure First */
|
||||
animation.addCallBackFunction('System Event Handler Physics', systemEventHandler.physics)
|
||||
|
||||
/* Spcaces Physics */
|
||||
animation.addCallBackFunction('CockpitSpace Physics', thisObject.cockpitSpace.physics)
|
||||
animation.addCallBackFunction('Floating Space Physics', thisObject.floatingSpace.physics)
|
||||
animation.addCallBackFunction('Charting Space Physics', thisObject.chartingSpace.physics)
|
||||
animation.addCallBackFunction('Design Space Physics', thisObject.designSpace.physics)
|
||||
animation.addCallBackFunction('Panels Space Physics', thisObject.panelsSpace.physics)
|
||||
|
||||
/* Spcaces Drawing */
|
||||
animation.addCallBackFunction('Floating Space Draw', thisObject.floatingSpace.draw)
|
||||
animation.addCallBackFunction('Charting Space Draw', thisObject.chartingSpace.draw)
|
||||
animation.addCallBackFunction('Panels Space', thisObject.panelsSpace.draw)
|
||||
animation.addCallBackFunction('CockpitSpace Draw', thisObject.cockpitSpace.draw)
|
||||
animation.addCallBackFunction('Design Space Draw', thisObject.designSpace.draw)
|
||||
animation.addCallBackFunction('Top Space Draw', thisObject.topSpace.draw)
|
||||
animation.addCallBackFunction('Splash Screen Draw', splashScreen.draw)
|
||||
animation.start()
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function initializeBrowserCanvas () {
|
||||
try {
|
||||
browserCanvasContext = browserCanvas.getContext('2d')
|
||||
browserCanvasContext.font = 'Saira Condensed'
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initializeBrowserCanvas -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function addCanvasEvents () {
|
||||
try {
|
||||
canvas.eventHandler.listenToEvent('Browser Resized', browserResized)
|
||||
|
||||
/* Keyboard events */
|
||||
window.addEventListener('keydown', onKeyDown, true)
|
||||
window.addEventListener('keyup', onKeyUp, true)
|
||||
|
||||
/* Mouse Events */
|
||||
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)
|
||||
|
||||
if (browserCanvas.addEventListener) {
|
||||
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) {
|
||||
e.preventDefault()
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] addCanvasEvents -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function browserResized () {
|
||||
try {
|
||||
browserCanvas = document.getElementById('canvas')
|
||||
|
||||
browserCanvas.width = window.innerWidth
|
||||
browserCanvas.height = window.innerHeight - CURRENT_TOP_MARGIN
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] browserResized -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function checkMediaRecording (event) {
|
||||
if ((event.ctrlKey === true || event.metaKey === true)) {
|
||||
let constructorParams
|
||||
switch (event.keyCode) {
|
||||
case 119: { // F8
|
||||
downloadCanvas('Superalgos.image.capture')
|
||||
return
|
||||
break
|
||||
}
|
||||
case 120: { // F9
|
||||
constructorParams = { format: 'gif', workersPath: 'externalScripts/', framerate: 8, name: 'Superalgos.video.capture'}
|
||||
break
|
||||
}
|
||||
case 121: { // F10
|
||||
constructorParams = { format: 'webm', framerate: 8, name: 'Superalgos.video.capture' }
|
||||
break
|
||||
}
|
||||
default: return
|
||||
}
|
||||
|
||||
if (areWeRecording === false) {
|
||||
console.log('RECORDING', constructorParams.format)
|
||||
mediaRecorder = new CCapture(constructorParams)
|
||||
mediaRecorder.start()
|
||||
areWeRecording = true
|
||||
} else {
|
||||
console.log('SAVING', constructorParams.format)
|
||||
areWeRecording = false
|
||||
mediaRecorder.stop()
|
||||
mediaRecorder.save()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onKeyUp (event) {
|
||||
thisObject.mouse.event = event
|
||||
thisObject.mouse.action = 'key up'
|
||||
}
|
||||
|
||||
function onKeyDown (event) {
|
||||
thisObject.mouse.event = event
|
||||
thisObject.mouse.action = 'key down'
|
||||
|
||||
checkMediaRecording(event)
|
||||
|
||||
let nodeOnFocus = canvas.designSpace.workspace.getNodeThatIsOnFocus()
|
||||
if (nodeOnFocus !== undefined) {
|
||||
if (nodeOnFocus.payload.uiObject.codeEditor !== undefined) {
|
||||
if (nodeOnFocus.payload.uiObject.codeEditor.visible === true) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (nodeOnFocus.payload.uiObject.configEditor !== undefined) {
|
||||
if (nodeOnFocus.payload.uiObject.configEditor.visible === true) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (nodeOnFocus.payload.uiObject.formulaEditor !== undefined) {
|
||||
if (nodeOnFocus.payload.uiObject.formulaEditor.visible === true) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (nodeOnFocus.payload.uiObject.conditionEditor !== undefined) {
|
||||
if (nodeOnFocus.payload.uiObject.conditionEditor.visible === true) {
|
||||
return
|
||||
}
|
||||
}
|
||||
if (nodeOnFocus.payload.uiObject.uiObjectTitle !== undefined) {
|
||||
if (nodeOnFocus.payload.uiObject.uiObjectTitle.editMode === true) {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ((event.ctrlKey === true || event.metaKey === true) && event.altKey === true && event.shiftKey === true && event.keyCode === 123) { // Dev Tool when used with F12
|
||||
if (nodeOnFocus !== undefined) {
|
||||
console.log(nodeOnFocus)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ((event.ctrlKey === true || event.metaKey === true) && event.altKey === true && event.shiftKey === true && event.keyCode === 122) { // Dev Tool when used with F11
|
||||
if (SHOW_ANIMATION_PERFORMACE === false) {
|
||||
SHOW_ANIMATION_PERFORMACE = true
|
||||
} else {
|
||||
SHOW_ANIMATION_PERFORMACE = false
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowUp') {
|
||||
thisObject.cockpitSpace.toTop()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowDown') {
|
||||
thisObject.cockpitSpace.toBottom()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowLeft') {
|
||||
thisObject.cockpitSpace.moveUp()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey === true && (event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowRight') {
|
||||
thisObject.cockpitSpace.moveDown()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey === true && event.ctrlKey === false && event.code === 'ArrowLeft') {
|
||||
canvas.chartingSpace.oneScreenLeft()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey === true && event.ctrlKey === false && event.code === 'ArrowRight') {
|
||||
canvas.chartingSpace.oneScreenRight()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey === true && event.code === 'ArrowUp') {
|
||||
canvas.chartingSpace.oneScreenUp()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.shiftKey === true && event.code === 'ArrowDown') {
|
||||
canvas.chartingSpace.oneScreenDown()
|
||||
return
|
||||
}
|
||||
|
||||
if ((event.ctrlKey === true || event.metaKey === true) && event.shiftKey === false && event.code === 'ArrowLeft') {
|
||||
let displaceVector = canvas.floatingSpace.oneScreenLeft()
|
||||
dragVector.downX = dragVector.downX + displaceVector.x
|
||||
dragVector.downY = dragVector.downY + displaceVector.y
|
||||
checkDrag()
|
||||
return
|
||||
}
|
||||
|
||||
if ((event.ctrlKey === true || event.metaKey === true) && event.shiftKey === false && event.code === 'ArrowRight') {
|
||||
let displaceVector = canvas.floatingSpace.oneScreenRight()
|
||||
dragVector.downX = dragVector.downX + displaceVector.x
|
||||
dragVector.downY = dragVector.downY + displaceVector.y
|
||||
checkDrag()
|
||||
return
|
||||
}
|
||||
|
||||
if ((event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowUp') {
|
||||
let displaceVector = canvas.floatingSpace.oneScreenUp()
|
||||
dragVector.downX = dragVector.downX + displaceVector.x
|
||||
dragVector.downY = dragVector.downY + displaceVector.y
|
||||
checkDrag()
|
||||
return
|
||||
}
|
||||
|
||||
if ((event.ctrlKey === true || event.metaKey === true) && event.code === 'ArrowDown') {
|
||||
let displaceVector = canvas.floatingSpace.oneScreenDown()
|
||||
dragVector.downX = dragVector.downX + displaceVector.x
|
||||
dragVector.downY = dragVector.downY + displaceVector.y
|
||||
checkDrag()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.code === 'Period') {
|
||||
if (nodeOnFocus !== undefined) {
|
||||
nodeOnFocus.payload.uiObject.setValue('Id: ' + nodeOnFocus.id)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if ((event.ctrlKey === true || event.metaKey === true) && event.altKey === true) {
|
||||
if ((event.keyCode >= 48 && event.keyCode <= 57) || (event.keyCode >= 65 && event.keyCode <= 90)) {
|
||||
/* From here we prevent the default behaviour */
|
||||
event.preventDefault()
|
||||
|
||||
let nodeUsingThisKey = canvas.designSpace.workspace.getNodeByShortcutKey(event.key)
|
||||
|
||||
if (nodeOnFocus === undefined && nodeUsingThisKey !== undefined) {
|
||||
/* Then we displace the whole workspace to center it at the node using this key */
|
||||
nodeUsingThisKey = canvas.floatingSpace.positionAtNode(nodeUsingThisKey)
|
||||
return
|
||||
}
|
||||
|
||||
/* If there is a node in focus, we try to assign the key to it. */
|
||||
if (nodeUsingThisKey !== undefined && nodeOnFocus !== undefined) {
|
||||
if (nodeUsingThisKey.id === nodeOnFocus.id) {
|
||||
nodeOnFocus.payload.uiObject.shortcutKey = ''
|
||||
nodeOnFocus.payload.uiObject.setValue('Shortcut Key Removed ')
|
||||
return
|
||||
} else {
|
||||
if (lastShortcutKeyRejection !== event.key + nodeUsingThisKey.type + ' ' + nodeUsingThisKey.name) {
|
||||
/* The first time we show a warning that this key is in use. */
|
||||
nodeOnFocus.payload.uiObject.setErrorMessage('Key already in use by ' + nodeUsingThisKey.type + ' ' + nodeUsingThisKey.name)
|
||||
lastShortcutKeyRejection = event.key + nodeUsingThisKey.type + ' ' + nodeUsingThisKey.name
|
||||
return
|
||||
} else {
|
||||
/* After the warning, we allow the key to be re-assigned */
|
||||
nodeUsingThisKey.payload.uiObject.shortcutKey = ''
|
||||
nodeUsingThisKey === undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
/* If there is not node using this key and a node in focus, we assign this key to this node */
|
||||
if (nodeUsingThisKey === undefined && nodeOnFocus !== undefined) {
|
||||
nodeOnFocus.payload.uiObject.shortcutKey = event.key
|
||||
nodeOnFocus.payload.uiObject.setValue('Shortcut Key: Ctrl + Alt + ' + event.key)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if (event.ctrlKey === true && event.altKey === true && nodeOnFocus !== undefined) {
|
||||
if (nodeOnFocus.payload.uiObject.shortcutKey !== undefined && nodeOnFocus.payload.uiObject.shortcutKey !== '') {
|
||||
nodeOnFocus.payload.uiObject.setValue('Shortcut Key: Ctrl + Alt + ' + nodeOnFocus.payload.uiObject.shortcutKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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.designSpace.workspace.spawn(reader.result, mousePosition)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onDragDrop -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseDown (event) {
|
||||
try {
|
||||
thisObject.mouse.event = event
|
||||
thisObject.mouse.position.x = event.pageX
|
||||
thisObject.mouse.position.y = event.pageY - CURRENT_TOP_MARGIN
|
||||
switch (event.buttons) {
|
||||
case 1: {
|
||||
thisObject.mouse.action = 'clicking with left button'
|
||||
break
|
||||
}
|
||||
case 2: {
|
||||
thisObject.mouse.action = 'clicking with right button'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let point = event
|
||||
point.x = event.pageX
|
||||
point.y = event.pageY - CURRENT_TOP_MARGIN
|
||||
|
||||
dragVector.downX = point.x
|
||||
dragVector.downY = point.y
|
||||
|
||||
let container
|
||||
|
||||
/* We check if the mouse is over an element of the Design Space / */
|
||||
|
||||
container = thisObject.designSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isDraggeable === true) {
|
||||
containerBeingDragged = container
|
||||
containerDragStarted = true
|
||||
containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point)
|
||||
return
|
||||
}
|
||||
|
||||
if (container !== undefined && container.isDraggeable === false) {
|
||||
return
|
||||
}
|
||||
|
||||
/* We check if the mouse is over an element of the Top Space / */
|
||||
|
||||
container = thisObject.topSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isDraggeable === true) {
|
||||
containerBeingDragged = container
|
||||
containerDragStarted = true
|
||||
containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point)
|
||||
return
|
||||
}
|
||||
|
||||
/* We check if the mouse is over an element of the CockpitSpace / */
|
||||
|
||||
container = thisObject.cockpitSpace.getContainer(point)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/* We check if the mouse is over a panel/ */
|
||||
|
||||
container = thisObject.panelsSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isDraggeable === true && event.shiftKey === false) {
|
||||
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
|
||||
}
|
||||
|
||||
/* we check if it is over any of the existing containers at the Charting Space. */
|
||||
|
||||
container = thisObject.chartingSpace.getContainer(point, GET_CONTAINER_PURPOSE.DRAGGING)
|
||||
|
||||
if (container !== undefined) {
|
||||
if (container.isDraggeable === true) {
|
||||
containerBeingDragged = container
|
||||
containerDragStarted = true
|
||||
containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point)
|
||||
return
|
||||
} else {
|
||||
if (container.isClickeable === false) {
|
||||
viewPortBeingDragged = true
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
container = thisObject.floatingSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isDraggeable === true) {
|
||||
containerBeingDragged = container
|
||||
containerDragStarted = true
|
||||
floatingObjectDragStarted = true
|
||||
containerBeingDragged.eventHandler.raiseEvent('onDragStarted', point)
|
||||
return
|
||||
}
|
||||
|
||||
if (container !== undefined && container.isClickeable === true) {
|
||||
/* We dont want to mess up with the click */
|
||||
return
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onMouseDown -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseClick (event) {
|
||||
try {
|
||||
if (ignoreNextClick === true) {
|
||||
ignoreNextClick = false
|
||||
return
|
||||
}
|
||||
|
||||
let point = event
|
||||
point.x = event.pageX
|
||||
point.y = event.pageY - CURRENT_TOP_MARGIN
|
||||
|
||||
let container
|
||||
|
||||
/* We check if the mouse is over an element of the Design Space / */
|
||||
|
||||
container = thisObject.designSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isClickeable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseClick', point)
|
||||
return
|
||||
}
|
||||
|
||||
/* We check if the mouse is over an element of the Top Space / */
|
||||
|
||||
container = thisObject.topSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isClickeable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseClick', point)
|
||||
return
|
||||
}
|
||||
|
||||
/* We check if the mouse is over an element of the CockpitSpace / */
|
||||
|
||||
container = thisObject.cockpitSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isClickeable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseClick', point)
|
||||
return
|
||||
}
|
||||
|
||||
/* We check if the mouse is over a panel/ */
|
||||
|
||||
container = thisObject.panelsSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isClickeable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseClick', point)
|
||||
return
|
||||
}
|
||||
|
||||
/* If it is not, then we check if it is over any of the existing containers at the Charting Space. */
|
||||
|
||||
container = thisObject.chartingSpace.getContainer(point, GET_CONTAINER_PURPOSE.MOUSE_CLICK)
|
||||
|
||||
if (container !== undefined && container.isClickeable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseClick', point)
|
||||
return
|
||||
}
|
||||
|
||||
/* We check if the mouse is over a floatingObject/ */
|
||||
container = thisObject.floatingSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.isClickeable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseClick', point)
|
||||
return
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onMouseClick -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseOut (event) {
|
||||
deactivateDragging(event)
|
||||
|
||||
/* When the mouse leaves the canvas, our elements needs to react to the fact that the mouse is over a far away place */
|
||||
let thisEvent = {
|
||||
pageX: VERY_LARGE_NUMBER,
|
||||
pageY: VERY_LARGE_NUMBER
|
||||
}
|
||||
onMouseOver(thisEvent)
|
||||
}
|
||||
|
||||
function onMouseUp (event) {
|
||||
deactivateDragging(event)
|
||||
}
|
||||
|
||||
function onMouseMove (event) {
|
||||
try {
|
||||
thisObject.mouse.event = event
|
||||
thisObject.mouse.position.x = event.pageX
|
||||
thisObject.mouse.position.y = event.pageY - CURRENT_TOP_MARGIN
|
||||
thisObject.mouse.action = 'moving'
|
||||
|
||||
/* Processing the event */
|
||||
let point = event
|
||||
point.x = event.pageX
|
||||
point.y = event.pageY - CURRENT_TOP_MARGIN
|
||||
|
||||
if (canvas.chartingSpace.viewport !== undefined) {
|
||||
canvas.chartingSpace.viewport.mousePosition.x = point.x
|
||||
canvas.chartingSpace.viewport.mousePosition.y = point.y
|
||||
}
|
||||
|
||||
if (containerDragStarted === true || floatingObjectDragStarted === true || viewPortBeingDragged === true) {
|
||||
if (floatingObjectDragStarted === true) {
|
||||
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
|
||||
browserCanvas.style.cursor = 'auto'
|
||||
ignoreNextClick = true
|
||||
return
|
||||
}
|
||||
} else {
|
||||
containerBeingDragged.eventHandler.raiseEvent('onDragFinished', point)
|
||||
containerDragStarted = false
|
||||
containerBeingDragged = undefined
|
||||
floatingObjectDragStarted = false
|
||||
browserCanvas.style.cursor = 'auto'
|
||||
ignoreNextClick = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
dragVector.upX = point.x
|
||||
dragVector.upY = point.y
|
||||
|
||||
checkDrag(event)
|
||||
}
|
||||
onMouseOver(event)
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onMouseMove -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
try {
|
||||
let point = event
|
||||
point.x = event.pageX
|
||||
point.y = event.pageY - CURRENT_TOP_MARGIN
|
||||
|
||||
if (containerDragStarted === true) {
|
||||
containerBeingDragged.eventHandler.raiseEvent('onMouseOver', point)
|
||||
return
|
||||
}
|
||||
|
||||
/* Then we check who is the current object underneeth the mounse. */
|
||||
|
||||
let container
|
||||
|
||||
/* We check if the mouse is over an element of the Strategy Space / */
|
||||
if (thisObject.designSpace !== undefined) {
|
||||
container = thisObject.designSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.detectMouseOver === true) {
|
||||
containerFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* We check if the mouse is over an element of the Top Space / */
|
||||
if (thisObject.topSpace !== undefined) {
|
||||
container = thisObject.topSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.detectMouseOver === true) {
|
||||
containerFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* We check if the mouse is over an element of the CockpitSpace / */
|
||||
if (thisObject.cockpitSpace !== undefined) {
|
||||
container = thisObject.cockpitSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.detectMouseOver === true) {
|
||||
containerFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* We check if the mouse is over a panel/ */
|
||||
if (thisObject.panelsSpace !== undefined) {
|
||||
container = thisObject.panelsSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.detectMouseOver === true) {
|
||||
containerFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* If it is not, then we check if it is over any of the existing containers at the Charting Space. */
|
||||
if (thisObject.chartingSpace !== undefined) {
|
||||
container = thisObject.chartingSpace.getContainer(point, GET_CONTAINER_PURPOSE.MOUSE_OVER)
|
||||
|
||||
if (container !== undefined && container.detectMouseOver === true) {
|
||||
containerFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* We check if the mouse is over a floatingObject/ */
|
||||
if (thisObject.floatingSpace !== undefined) {
|
||||
container = thisObject.floatingSpace.getContainer(point)
|
||||
|
||||
if (container !== undefined && container.detectMouseOver === true) {
|
||||
containerFound()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function containerFound () {
|
||||
if (lastContainerMouseOver !== undefined) {
|
||||
if (container.id !== lastContainerMouseOver.id) {
|
||||
lastContainerMouseOver.eventHandler.raiseEvent('onMouseNotOver', point)
|
||||
}
|
||||
}
|
||||
container.eventHandler.raiseEvent('onMouseOver', point)
|
||||
lastContainerMouseOver = container
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onMouseOver -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseWheel (event) {
|
||||
try {
|
||||
thisObject.mouse.event = event
|
||||
thisObject.mouse.position.x = event.pageX
|
||||
thisObject.mouse.position.y = event.pageY - CURRENT_TOP_MARGIN
|
||||
thisObject.mouse.action = 'wheel'
|
||||
|
||||
// cross-browser wheel delta
|
||||
var event = window.event || event // old IE support
|
||||
event.delta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail))
|
||||
|
||||
/* We try first with panels. */
|
||||
|
||||
let point = {
|
||||
x: event.pageX,
|
||||
y: event.pageY - CURRENT_TOP_MARGIN
|
||||
}
|
||||
|
||||
event.mousePosition = point
|
||||
let container
|
||||
container = canvas.panelsSpace.getContainer({ x: point.x, y: point.y })
|
||||
|
||||
if (container !== undefined && container.isWheelable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseWheel', event)
|
||||
return false // This instructs the browser not to take the event and scroll the page.
|
||||
}
|
||||
|
||||
/* We try the CockpitSpace. */
|
||||
|
||||
container = canvas.cockpitSpace.getContainer({ x: point.x, y: point.y })
|
||||
|
||||
if (container !== undefined && container.isWheelable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseWheel', event)
|
||||
return false // This instructs the browser not to take the event and scroll the page.
|
||||
}
|
||||
|
||||
/* Charting Space. */
|
||||
|
||||
container = canvas.chartingSpace.getContainer({ x: point.x, y: point.y }, GET_CONTAINER_PURPOSE.MOUSE_WHEEL)
|
||||
if (container !== undefined && container.isWheelable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseWheel', event)
|
||||
return false // This instructs the browser not to take the event and scroll the page.
|
||||
}
|
||||
|
||||
if (container !== undefined) {
|
||||
canvas.chartingSpace.viewport.onMouseWheel(event)
|
||||
return false
|
||||
}
|
||||
|
||||
/* We try second with floating objects. */
|
||||
|
||||
container = canvas.floatingSpace.getContainer({ x: point.x, y: point.y })
|
||||
|
||||
if (container !== undefined && container.isWheelable === true) {
|
||||
container.eventHandler.raiseEvent('onMouseWheel', event)
|
||||
return false // This instructs the browser not to take the event and scroll the page.
|
||||
}
|
||||
|
||||
return false
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onMouseWheel -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
thisObject.mouse.event = event
|
||||
thisObject.mouse.position.x = event.pageX
|
||||
thisObject.mouse.position.y = event.pageY - CURRENT_TOP_MARGIN
|
||||
thisObject.mouse.action = 'dragging'
|
||||
|
||||
browserCanvas.style.cursor = 'grabbing'
|
||||
thisObject.eventHandler.raiseEvent('Dragging', undefined)
|
||||
|
||||
if (viewPortBeingDragged) {
|
||||
let displaceVector = {
|
||||
x: dragVector.upX - dragVector.downX,
|
||||
y: dragVector.upY - dragVector.downY
|
||||
}
|
||||
|
||||
canvas.chartingSpace.viewport.displace(displaceVector)
|
||||
}
|
||||
if (containerDragStarted) {
|
||||
if (containerBeingDragged !== undefined) {
|
||||
if (containerBeingDragged.space === 'Charting Space') {
|
||||
let downCopy = {
|
||||
x: dragVector.downX,
|
||||
y: dragVector.downY
|
||||
}
|
||||
|
||||
let downNoZoom
|
||||
downNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(downCopy)
|
||||
|
||||
let upCopy = {
|
||||
x: dragVector.upX,
|
||||
y: dragVector.upY
|
||||
}
|
||||
|
||||
let upNoZoom
|
||||
upNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(upCopy)
|
||||
|
||||
displaceVector = {
|
||||
x: upNoZoom.x - downNoZoom.x,
|
||||
y: upNoZoom.y - downNoZoom.y
|
||||
}
|
||||
|
||||
let moveSucceed = containerBeingDragged.displace(displaceVector)
|
||||
if (moveSucceed === false) {
|
||||
deactivateDragging(event)
|
||||
}
|
||||
} else {
|
||||
let displaceVector = {
|
||||
x: dragVector.upX - dragVector.downX,
|
||||
y: dragVector.upY - dragVector.downY
|
||||
}
|
||||
let moveSucceed = containerBeingDragged.displace(displaceVector)
|
||||
if (moveSucceed === false) {
|
||||
deactivateDragging(event)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Finally we set the starting point of the new dragVector at this current point. */
|
||||
dragVector.downX = dragVector.upX
|
||||
dragVector.downY = dragVector.upY
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] checkDrag -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,139 @@
|
||||
|
||||
function getSideDays (timeFrame) {
|
||||
let daysOnSides
|
||||
|
||||
if (timeFrame < _1_HOUR_IN_MILISECONDS) {
|
||||
daysOnSides = 1
|
||||
}
|
||||
|
||||
if (timeFrame === _1_HOUR_IN_MILISECONDS) {
|
||||
daysOnSides = 9
|
||||
}
|
||||
|
||||
if (timeFrame === _2_HOURS_IN_MILISECONDS) {
|
||||
daysOnSides = 10
|
||||
}
|
||||
|
||||
if (timeFrame === _3_HOURS_IN_MILISECONDS) {
|
||||
daysOnSides = 12
|
||||
}
|
||||
|
||||
if (timeFrame === _4_HOURS_IN_MILISECONDS) {
|
||||
daysOnSides = 20
|
||||
}
|
||||
|
||||
if (timeFrame === _6_HOURS_IN_MILISECONDS) {
|
||||
daysOnSides = 35
|
||||
}
|
||||
|
||||
if (timeFrame === _8_HOURS_IN_MILISECONDS) {
|
||||
daysOnSides = 44
|
||||
}
|
||||
|
||||
if (timeFrame === _12_HOURS_IN_MILISECONDS) {
|
||||
daysOnSides = 56
|
||||
}
|
||||
|
||||
if (timeFrame === ONE_DAY_IN_MILISECONDS) {
|
||||
daysOnSides = 154
|
||||
}
|
||||
|
||||
return daysOnSides
|
||||
}
|
||||
|
||||
function getCondenseFactor (timeFrame) {
|
||||
switch (timeFrame) {
|
||||
case _1_MINUTE_IN_MILISECONDS: return 1
|
||||
case _5_MINUTES_IN_MILISECONDS: return 5
|
||||
case _10_MINUTES_IN_MILISECONDS: return 10
|
||||
case _30_MINUTES_IN_MILISECONDS: return 50
|
||||
case _1_HOUR_IN_MILISECONDS: return 100
|
||||
}
|
||||
}
|
||||
|
||||
function getTransparenceFactor (timeFrame) {
|
||||
switch (timeFrame) {
|
||||
case _1_MINUTE_IN_MILISECONDS: return '0.12'
|
||||
case _5_MINUTES_IN_MILISECONDS: return '0.10'
|
||||
case _10_MINUTES_IN_MILISECONDS: return '0.08'
|
||||
case _30_MINUTES_IN_MILISECONDS: return '0.06'
|
||||
case _1_HOUR_IN_MILISECONDS: return '0.04'
|
||||
}
|
||||
}
|
||||
|
||||
function recalculatePeriod (zoomLevel) {
|
||||
// console.log('NEW ZOOM LEVEL', zoomLevel)
|
||||
if (zoomLevel > 400) {
|
||||
return _1_MINUTE_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 360) {
|
||||
return _2_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 330) {
|
||||
return _3_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 300) {
|
||||
return _4_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 270) {
|
||||
return _5_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 250) {
|
||||
return _10_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 230) {
|
||||
return _15_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 210) {
|
||||
return _20_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 190) {
|
||||
return _30_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 170) {
|
||||
return _40_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 150) {
|
||||
return _45_MINUTES_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 130) {
|
||||
return _1_HOUR_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 110) {
|
||||
return _2_HOURS_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 80) {
|
||||
return _3_HOURS_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 50) {
|
||||
return _4_HOURS_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 35) {
|
||||
return _6_HOURS_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 20) {
|
||||
return _8_HOURS_IN_MILISECONDS
|
||||
}
|
||||
|
||||
if (zoomLevel > 10) {
|
||||
return _12_HOURS_IN_MILISECONDS
|
||||
}
|
||||
|
||||
return ONE_DAY_IN_MILISECONDS
|
||||
}
|
||||
@@ -0,0 +1,262 @@
|
||||
/*
|
||||
|
||||
This Object object is responsible for defining the coordinate system in a TimeLine.
|
||||
|
||||
The scale on the X axis depends on the time.
|
||||
The scale on the Y axis depends on the data that wants to be plotted. Usually we need the Max amount on that data to generate a proper scale.
|
||||
|
||||
*/
|
||||
|
||||
function newCoordinateSystem () {
|
||||
let thisObject = {
|
||||
min: undefined,
|
||||
max: undefined,
|
||||
maxHeight: undefined,
|
||||
maxWidth: undefined,
|
||||
scale: undefined,
|
||||
eventHandler: undefined,
|
||||
autoMinXScale: true,
|
||||
autoMaxXScale: true,
|
||||
autoMinYScale: true,
|
||||
autoMaxYScale: true,
|
||||
physics: physics,
|
||||
reportXValue: reportXValue,
|
||||
reportYValue: reportYValue,
|
||||
zoomX: zoomX,
|
||||
zoomY: zoomY,
|
||||
recalculateScale: recalculateScale,
|
||||
transformThisPoint: transformThisPoint,
|
||||
transformThisPoint2: transformThisPoint2,
|
||||
unInverseTransform: unInverseTransform,
|
||||
inverseTransformUncappedY: inverseTransformUncappedY,
|
||||
initializeX: initializeX,
|
||||
initializeY: initializeY,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.min = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
thisObject.max = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
thisObject.scale = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
thisObject.eventHandler = newEventHandler()
|
||||
thisObject.eventHandler.name = 'Coordinate System'
|
||||
|
||||
let newXMax = -VERY_LARGE_NUMBER
|
||||
let newXMin = VERY_LARGE_NUMBER
|
||||
let noXValueReported = true
|
||||
|
||||
let newYMax = -VERY_LARGE_NUMBER
|
||||
let newYMin = VERY_LARGE_NUMBER
|
||||
let noYValueReported = true
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.eventHandler.finalize()
|
||||
thisObject.eventHandler = undefined
|
||||
}
|
||||
|
||||
function initialize (minValue, maxValue, pMaxWidth, pMaxHeight) {
|
||||
/* Defines the min and max value of rate that we are going to transport to the available screen at the center position. */
|
||||
thisObject.min.x = minValue.x
|
||||
thisObject.max.x = maxValue.x
|
||||
|
||||
thisObject.min.y = minValue.y
|
||||
thisObject.max.y = maxValue.y
|
||||
|
||||
thisObject.maxWidth = pMaxWidth
|
||||
thisObject.maxHeight = pMaxHeight
|
||||
|
||||
recalculateScale()
|
||||
}
|
||||
|
||||
function physics () {
|
||||
let mustRecalculate = false
|
||||
|
||||
if ((thisObject.autoMinXScale === true || thisObject.autoMaxXScale === true) && noXValueReported === false) {
|
||||
if (thisObject.autoMaxXScale === true && thisObject.max.x !== newXMax) {
|
||||
thisObject.max.x = newXMax
|
||||
mustRecalculate = true
|
||||
}
|
||||
|
||||
if (thisObject.autoMaxXScale === true) {
|
||||
newXMax = -VERY_LARGE_NUMBER
|
||||
}
|
||||
|
||||
if (thisObject.autoMinXScale === true && thisObject.min.x !== newXMin) {
|
||||
thisObject.min.x = newXMin
|
||||
newXMin = VERY_LARGE_NUMBER
|
||||
mustRecalculate = true
|
||||
}
|
||||
|
||||
if (thisObject.autoMinXScale === true) {
|
||||
newXMin = VERY_LARGE_NUMBER
|
||||
}
|
||||
|
||||
/* Reseting this to start over at each cycle. */
|
||||
noXValueReported = true
|
||||
}
|
||||
|
||||
if ((thisObject.autoMinYScale === true || thisObject.autoMaxYScale === true) && noYValueReported === false) {
|
||||
if (thisObject.autoMaxYScale === true && thisObject.max.y !== newYMax) {
|
||||
thisObject.max.y = newYMax
|
||||
mustRecalculate = true
|
||||
}
|
||||
|
||||
if (thisObject.autoMaxYScale === true) {
|
||||
newYMax = -VERY_LARGE_NUMBER
|
||||
}
|
||||
|
||||
if (thisObject.autoMinYScale === true && thisObject.min.y !== newYMin) {
|
||||
thisObject.min.y = newYMin
|
||||
newYMin = VERY_LARGE_NUMBER
|
||||
mustRecalculate = true
|
||||
}
|
||||
|
||||
if (thisObject.autoMinYScale === true) {
|
||||
newYMin = VERY_LARGE_NUMBER
|
||||
}
|
||||
|
||||
/* Reseting this to start over at each cycle. */
|
||||
noYValueReported = true
|
||||
}
|
||||
|
||||
if (mustRecalculate === true) {
|
||||
recalculateScale()
|
||||
}
|
||||
}
|
||||
|
||||
function reportXValue (value) {
|
||||
if (thisObject.autoMinXScale === true) {
|
||||
if (value < newXMin) { newXMin = value }
|
||||
noXValueReported = false
|
||||
}
|
||||
if (thisObject.autoMaxXScale === true) {
|
||||
if (value > newXMax) { newXMax = value }
|
||||
noXValueReported = false
|
||||
}
|
||||
}
|
||||
|
||||
function reportYValue (value) {
|
||||
if (thisObject.autoMinYScale === true) {
|
||||
if (value < newYMin) { newYMin = value }
|
||||
noYValueReported = false
|
||||
}
|
||||
if (thisObject.autoMaxYScale === true) {
|
||||
if (value > newYMax) { newYMax = value }
|
||||
noYValueReported = false
|
||||
}
|
||||
}
|
||||
|
||||
function zoomX (factor, mousePosition, container) {
|
||||
let mouseAtCointainer = unTransformThisPoint(mousePosition, container)
|
||||
let leftDistance = mouseAtCointainer.x
|
||||
let rightDistance = container.frame.width - mouseAtCointainer.x
|
||||
let diff = thisObject.max.x - thisObject.min.x
|
||||
let min = thisObject.min.x + diff * factor * leftDistance / (container.frame.width / 2)
|
||||
let max = thisObject.max.x - diff * factor * rightDistance / (container.frame.width / 2)
|
||||
|
||||
if (min < max) {
|
||||
thisObject.min.x = min
|
||||
thisObject.max.x = max
|
||||
thisObject.recalculateScale()
|
||||
}
|
||||
}
|
||||
|
||||
function zoomY (factor, mousePosition, container) {
|
||||
let mouseAtCointainer = unTransformThisPoint(mousePosition, container)
|
||||
let topDistance = mouseAtCointainer.y
|
||||
let bottomDistance = container.frame.height - mouseAtCointainer.y
|
||||
let diff = thisObject.max.y - thisObject.min.y
|
||||
let min = thisObject.min.y + diff * factor * bottomDistance / (container.frame.height / 2)
|
||||
let max = thisObject.max.y - diff * factor * topDistance / (container.frame.height / 2)
|
||||
|
||||
if (min < max) {
|
||||
thisObject.min.y = min
|
||||
thisObject.max.y = max
|
||||
thisObject.recalculateScale()
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateScale (event) {
|
||||
thisObject.scale.x = thisObject.maxWidth / (thisObject.max.x - thisObject.min.x)
|
||||
thisObject.scale.y = thisObject.maxHeight / (thisObject.max.y - thisObject.min.y)
|
||||
|
||||
thisObject.eventHandler.raiseEvent('Scale Changed', event)
|
||||
}
|
||||
|
||||
function initializeX (minValue, maxValue, maxWidth) {
|
||||
thisObject.min.x = minValue.x // * 0.999; // 0.1% less
|
||||
thisObject.max.x = maxValue.x // * 1.001; // 0.1% more
|
||||
|
||||
thisObject.scale.x = thisObject.maxWidth / (thisObject.max.x - thisObject.min.x)
|
||||
}
|
||||
|
||||
function initializeY (minValue, maxValue, pMaxHeight) {
|
||||
thisObject.min.y = minValue.y // * 0.999; // 0.1% less
|
||||
thisObject.max.y = maxValue.y // * 1.001; // 0.1% more
|
||||
|
||||
thisObject.scale.y = pMaxHeight / (thisObject.max.y - thisObject.min.y)
|
||||
|
||||
thisObject.maxHeight = pMaxHeight
|
||||
}
|
||||
|
||||
function transformThisPoint (point) {
|
||||
/*
|
||||
|
||||
This is the straigh fordward transformation this object provides. The input is a point on the data set that wants to be plotted in a timeline.
|
||||
The x value of the point must be a datetime.valueOf() and is going to be transformed into an x value on the coordinate system of the timeline.
|
||||
The y value should be a value in the dataset at that moment in time. Depending on how this coordinate system was initialized then are the
|
||||
possible values y can have. For sure it must be in the range between the min and max y declared at initialization. The y value has a special
|
||||
treatment since the browser canvas object used, has a zero y value at the top, while the timeline we use has a zero y value at the botton,
|
||||
for that reason y is flipped.
|
||||
|
||||
Besides this, what we do is to multiply by the scale, which in turn is calculated at initialization time depending on the values provided.
|
||||
|
||||
*/
|
||||
|
||||
point = {
|
||||
x: (point.x - thisObject.min.x) * thisObject.scale.x,
|
||||
y: thisObject.maxHeight - (point.y - thisObject.min.y) * thisObject.scale.y
|
||||
}
|
||||
|
||||
return point
|
||||
}
|
||||
|
||||
function transformThisPoint2 (point) {
|
||||
point = {
|
||||
x: (point.x - thisObject.min.x) * thisObject.scale.x,
|
||||
y: (thisObject.maxHeight - point.y - thisObject.min.y) * thisObject.scale.y
|
||||
}
|
||||
|
||||
return point
|
||||
}
|
||||
|
||||
function unInverseTransform (point, inverseY) {
|
||||
point = {
|
||||
x: (point.x / thisObject.scale.x) + thisObject.min.x,
|
||||
y: (inverseY - point.y) / thisObject.scale.y + thisObject.min.y
|
||||
}
|
||||
|
||||
return point
|
||||
}
|
||||
|
||||
function inverseTransformUncappedY (point, inverseY) {
|
||||
point = {
|
||||
x: (point.x - thisObject.min.x) * thisObject.scale.x,
|
||||
y: (inverseY - point.y) * thisObject.scale.y
|
||||
}
|
||||
|
||||
return point
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,475 @@
|
||||
function newEdgeEditor () {
|
||||
const MODULE_NAME = 'Edge Editor'
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
isVisible: true,
|
||||
physics: physics,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
const EDGE_SIZE = 6
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
|
||||
thisObject.container.isDraggeable = true
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isWheelable = false
|
||||
thisObject.container.detectMouseOver = true
|
||||
|
||||
let buttonUsedForDragging
|
||||
let isMouseOver
|
||||
let whereIsMouseOver = 'outside'
|
||||
|
||||
let coordinateSystem
|
||||
|
||||
let onMouseOverEventSubscriptionId
|
||||
let onMouseNotOverEventSubscriptionId
|
||||
let onDragStartedEventSubscriptionId
|
||||
|
||||
let mouse = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onDragStartedEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
mouse = undefined
|
||||
}
|
||||
|
||||
function initialize (pCoordinateSystem) {
|
||||
coordinateSystem = pCoordinateSystem
|
||||
|
||||
onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
||||
onDragStartedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDragStarted', onDragStarted)
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
isMouseOver = true
|
||||
mouse = {
|
||||
position: {
|
||||
x: event.x,
|
||||
y: event.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseNotOver () {
|
||||
isMouseOver = false
|
||||
}
|
||||
|
||||
function onDragStarted (event) {
|
||||
switch (event.buttons) {
|
||||
case 1: {
|
||||
buttonUsedForDragging = 'left mouse button'
|
||||
break
|
||||
}
|
||||
case 2: {
|
||||
buttonUsedForDragging = 'right mouse button'
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (event, purpose) {
|
||||
if (thisObject.container.frame.isThisPointHere(event, undefined, undefined, -EDGE_SIZE) === true && event.shiftKey !== true) {
|
||||
let point = {
|
||||
x: event.x,
|
||||
y: event.y
|
||||
}
|
||||
point = unTransformThisPoint(point, thisObject.container)
|
||||
|
||||
if (point.x <= 0) {
|
||||
whereIsMouseOver = 'left'
|
||||
return thisObject.container
|
||||
}
|
||||
|
||||
if (point.x >= thisObject.container.frame.width) {
|
||||
whereIsMouseOver = 'right'
|
||||
return thisObject.container
|
||||
}
|
||||
|
||||
if (point.y <= 0) {
|
||||
whereIsMouseOver = 'top'
|
||||
return thisObject.container
|
||||
}
|
||||
|
||||
if (point.y >= thisObject.container.frame.height) {
|
||||
whereIsMouseOver = 'bottom'
|
||||
return thisObject.container
|
||||
}
|
||||
|
||||
whereIsMouseOver = 'center'
|
||||
switch (purpose) {
|
||||
case GET_CONTAINER_PURPOSE.MOUSE_OVER:
|
||||
return // We will let the Time Machine to handle this situation.
|
||||
break
|
||||
case GET_CONTAINER_PURPOSE.DRAGGING:
|
||||
return thisObject.container
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (thisObject.container.frame.position.x === 0 && thisObject.container.frame.position.y === 0) { return }
|
||||
|
||||
let dragVector = {
|
||||
x: thisObject.container.frame.position.x,
|
||||
y: thisObject.container.frame.position.y
|
||||
}
|
||||
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
const MIN_WIDTH = 100
|
||||
const MIN_HEIGHT = 50
|
||||
|
||||
switch (whereIsMouseOver) {
|
||||
case 'center': {
|
||||
switch (buttonUsedForDragging) {
|
||||
case 'right mouse button': {
|
||||
/* This is equivalent to drag the whole Time Machine, so we will apply the translation received onto the Time Machine container. */
|
||||
thisObject.container.parentContainer.frame.position.x = thisObject.container.parentContainer.frame.position.x + dragVector.x
|
||||
thisObject.container.parentContainer.frame.position.y = thisObject.container.parentContainer.frame.position.y + dragVector.y
|
||||
thisObject.container.parentContainer.eventHandler.raiseEvent('onDisplace')
|
||||
break
|
||||
}
|
||||
case 'left mouse button': {
|
||||
let point = {
|
||||
x: -dragVector.x,
|
||||
y: -dragVector.y
|
||||
}
|
||||
let newMinDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem)
|
||||
let newMaxRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem)
|
||||
let xDifferenceMaxMin = coordinateSystem.max.x - coordinateSystem.min.x
|
||||
let yDifferenceMaxMin = coordinateSystem.max.y - coordinateSystem.min.y
|
||||
coordinateSystem.min.x = newMinDate.valueOf()
|
||||
coordinateSystem.max.x = newMinDate.valueOf() + xDifferenceMaxMin
|
||||
coordinateSystem.min.y = newMaxRate - yDifferenceMaxMin
|
||||
coordinateSystem.max.y = newMaxRate
|
||||
let event = {
|
||||
type: 'center dragged',
|
||||
dragVector: dragVector
|
||||
}
|
||||
coordinateSystem.recalculateScale(event)
|
||||
}
|
||||
}
|
||||
thisObject.container.parentContainer.eventHandler.raiseEvent('onMouseOver', mouse.position)
|
||||
break
|
||||
}
|
||||
case 'top' : {
|
||||
if (thisObject.container.parentContainer.frame.height - dragVector.y < MIN_HEIGHT && dragVector.y > 0) {
|
||||
return
|
||||
}
|
||||
|
||||
let point = {
|
||||
x: dragVector.x,
|
||||
y: dragVector.y
|
||||
}
|
||||
let newMaxRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem)
|
||||
|
||||
thisObject.container.parentContainer.frame.position.y = thisObject.container.parentContainer.frame.position.y + dragVector.y
|
||||
thisObject.container.parentContainer.frame.height = thisObject.container.parentContainer.frame.height - dragVector.y
|
||||
|
||||
switch (buttonUsedForDragging) {
|
||||
case 'left mouse button': {
|
||||
coordinateSystem.max.y = newMaxRate
|
||||
coordinateSystem.maxHeight = thisObject.container.parentContainer.frame.height
|
||||
let event = {
|
||||
type: 'top dragged',
|
||||
dragVector: dragVector
|
||||
}
|
||||
coordinateSystem.recalculateScale(event)
|
||||
break
|
||||
}
|
||||
case 'right mouse button': {
|
||||
coordinateSystem.maxHeight = thisObject.container.parentContainer.frame.height
|
||||
coordinateSystem.recalculateScale()
|
||||
break
|
||||
}
|
||||
}
|
||||
thisObject.container.parentContainer.eventHandler.raiseEvent('Dimmensions Changed', event)
|
||||
break
|
||||
}
|
||||
case 'bottom' : {
|
||||
if (thisObject.container.parentContainer.frame.height + dragVector.y < MIN_HEIGHT && dragVector.y < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
let point = {
|
||||
x: dragVector.x,
|
||||
y: dragVector.y + thisObject.container.frame.height
|
||||
}
|
||||
let newMinRate = getRateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem)
|
||||
|
||||
thisObject.container.parentContainer.frame.height = thisObject.container.parentContainer.frame.height + dragVector.y
|
||||
|
||||
switch (buttonUsedForDragging) {
|
||||
case 'left mouse button': {
|
||||
coordinateSystem.min.y = newMinRate
|
||||
coordinateSystem.maxHeight = thisObject.container.parentContainer.frame.height
|
||||
let event = {
|
||||
type: 'bottom dragged',
|
||||
dragVector: dragVector
|
||||
}
|
||||
coordinateSystem.recalculateScale(event)
|
||||
break
|
||||
}
|
||||
case 'right mouse button': {
|
||||
coordinateSystem.maxHeight = thisObject.container.parentContainer.frame.height
|
||||
coordinateSystem.recalculateScale()
|
||||
break
|
||||
}
|
||||
}
|
||||
thisObject.container.parentContainer.eventHandler.raiseEvent('Dimmensions Changed', event)
|
||||
break
|
||||
}
|
||||
case 'left' : {
|
||||
if (thisObject.container.parentContainer.frame.width - dragVector.x < MIN_WIDTH && dragVector.x > 0) {
|
||||
return
|
||||
}
|
||||
let point = {
|
||||
x: dragVector.x,
|
||||
y: dragVector.y
|
||||
}
|
||||
let newMinDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem)
|
||||
|
||||
thisObject.container.parentContainer.frame.position.x = thisObject.container.parentContainer.frame.position.x + dragVector.x
|
||||
thisObject.container.parentContainer.frame.width = thisObject.container.parentContainer.frame.width - dragVector.x
|
||||
|
||||
switch (buttonUsedForDragging) {
|
||||
case 'left mouse button': {
|
||||
coordinateSystem.min.x = newMinDate.valueOf()
|
||||
coordinateSystem.maxWidth = thisObject.container.parentContainer.frame.width
|
||||
coordinateSystem.recalculateScale()
|
||||
break
|
||||
}
|
||||
case 'right mouse button': {
|
||||
coordinateSystem.maxWidth = thisObject.container.parentContainer.frame.width
|
||||
coordinateSystem.recalculateScale()
|
||||
break
|
||||
}
|
||||
}
|
||||
thisObject.container.parentContainer.eventHandler.raiseEvent('Dimmensions Changed', event)
|
||||
break
|
||||
}
|
||||
case 'right' : {
|
||||
if (thisObject.container.parentContainer.frame.width + dragVector.x < MIN_WIDTH && dragVector.x < 0) {
|
||||
return
|
||||
}
|
||||
|
||||
let point = {
|
||||
x: dragVector.x + thisObject.container.frame.width,
|
||||
y: dragVector.y
|
||||
}
|
||||
let newMaxDate = getDateFromPointAtContainer(point, thisObject.container.parentContainer, coordinateSystem)
|
||||
|
||||
thisObject.container.parentContainer.frame.width = thisObject.container.parentContainer.frame.width + dragVector.x
|
||||
|
||||
switch (buttonUsedForDragging) {
|
||||
case 'left mouse button': {
|
||||
coordinateSystem.max.x = newMaxDate.valueOf()
|
||||
coordinateSystem.maxWidth = thisObject.container.parentContainer.frame.width
|
||||
coordinateSystem.recalculateScale()
|
||||
break
|
||||
}
|
||||
case 'right mouse button': {
|
||||
coordinateSystem.maxWidth = thisObject.container.parentContainer.frame.width
|
||||
coordinateSystem.recalculateScale()
|
||||
break
|
||||
}
|
||||
}
|
||||
thisObject.container.parentContainer.eventHandler.raiseEvent('Dimmensions Changed', event)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
let MARGIN = 1
|
||||
let edgeSize
|
||||
let lineWidth
|
||||
|
||||
if (whereIsMouseOver === 'outside' || isMouseOver === false) {
|
||||
edgeSize = EDGE_SIZE / 2
|
||||
lineWidth = 0.1
|
||||
} else {
|
||||
edgeSize = EDGE_SIZE
|
||||
lineWidth = 0.5
|
||||
}
|
||||
|
||||
const OPACITY = 1
|
||||
|
||||
pointA1 = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
pointA2 = {
|
||||
x: thisObject.container.frame.width,
|
||||
y: 0
|
||||
}
|
||||
|
||||
pointA3 = {
|
||||
x: thisObject.container.frame.width,
|
||||
y: thisObject.container.frame.height
|
||||
}
|
||||
|
||||
pointA4 = {
|
||||
x: 0,
|
||||
y: thisObject.container.frame.height
|
||||
}
|
||||
|
||||
pointB1 = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
pointB2 = {
|
||||
x: thisObject.container.frame.width,
|
||||
y: 0
|
||||
}
|
||||
|
||||
pointB3 = {
|
||||
x: thisObject.container.frame.width,
|
||||
y: thisObject.container.frame.height
|
||||
}
|
||||
|
||||
pointB4 = {
|
||||
x: 0,
|
||||
y: thisObject.container.frame.height
|
||||
}
|
||||
|
||||
pointA1 = transformThisPoint(pointA1, thisObject.container)
|
||||
pointA2 = transformThisPoint(pointA2, thisObject.container)
|
||||
pointA3 = transformThisPoint(pointA3, thisObject.container)
|
||||
pointA4 = transformThisPoint(pointA4, thisObject.container)
|
||||
|
||||
pointB1 = transformThisPoint(pointB1, thisObject.container)
|
||||
pointB2 = transformThisPoint(pointB2, thisObject.container)
|
||||
pointB3 = transformThisPoint(pointB3, thisObject.container)
|
||||
pointB4 = transformThisPoint(pointB4, thisObject.container)
|
||||
|
||||
pointA1 = {
|
||||
x: pointA1.x - MARGIN,
|
||||
y: pointA1.y - MARGIN
|
||||
}
|
||||
|
||||
pointA2 = {
|
||||
x: pointA2.x + MARGIN,
|
||||
y: pointA2.y - MARGIN
|
||||
}
|
||||
|
||||
pointA3 = {
|
||||
x: pointA3.x + MARGIN,
|
||||
y: pointA3.y + MARGIN
|
||||
}
|
||||
|
||||
pointA4 = {
|
||||
x: pointA4.x - MARGIN,
|
||||
y: pointA4.y + MARGIN
|
||||
}
|
||||
|
||||
pointB1 = {
|
||||
x: pointB1.x - edgeSize,
|
||||
y: pointB1.y - edgeSize
|
||||
}
|
||||
|
||||
pointB2 = {
|
||||
x: pointB2.x + edgeSize,
|
||||
y: pointB2.y - edgeSize
|
||||
}
|
||||
|
||||
pointB3 = {
|
||||
x: pointB3.x + edgeSize,
|
||||
y: pointB3.y + edgeSize
|
||||
}
|
||||
|
||||
pointB4 = {
|
||||
x: pointB4.x - edgeSize,
|
||||
y: pointB4.y + edgeSize
|
||||
}
|
||||
|
||||
pointA1 = canvas.chartingSpace.viewport.fitIntoViewport(pointA1)
|
||||
pointA2 = canvas.chartingSpace.viewport.fitIntoViewport(pointA2)
|
||||
pointA3 = canvas.chartingSpace.viewport.fitIntoViewport(pointA3)
|
||||
pointA4 = canvas.chartingSpace.viewport.fitIntoViewport(pointA4)
|
||||
|
||||
pointB1 = canvas.chartingSpace.viewport.fitIntoViewport(pointB1)
|
||||
pointB2 = canvas.chartingSpace.viewport.fitIntoViewport(pointB2)
|
||||
pointB3 = canvas.chartingSpace.viewport.fitIntoViewport(pointB3)
|
||||
pointB4 = canvas.chartingSpace.viewport.fitIntoViewport(pointB4)
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(pointA1.x, pointA1.y)
|
||||
browserCanvasContext.lineTo(pointA2.x, pointA2.y)
|
||||
browserCanvasContext.lineTo(pointB2.x, pointB2.y)
|
||||
browserCanvasContext.lineTo(pointB1.x, pointB1.y)
|
||||
browserCanvasContext.lineTo(pointA1.x, pointA1.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
drawEdge('top')
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(pointA2.x, pointA2.y)
|
||||
browserCanvasContext.lineTo(pointA3.x, pointA3.y)
|
||||
browserCanvasContext.lineTo(pointB3.x, pointB3.y)
|
||||
browserCanvasContext.lineTo(pointB2.x, pointB2.y)
|
||||
browserCanvasContext.lineTo(pointA2.x, pointA2.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
drawEdge('right')
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(pointA3.x, pointA3.y)
|
||||
browserCanvasContext.lineTo(pointA4.x, pointA4.y)
|
||||
browserCanvasContext.lineTo(pointB4.x, pointB4.y)
|
||||
browserCanvasContext.lineTo(pointB3.x, pointB3.y)
|
||||
browserCanvasContext.lineTo(pointA3.x, pointA3.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
drawEdge('bottom')
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(pointA4.x, pointA4.y)
|
||||
browserCanvasContext.lineTo(pointA1.x, pointA1.y)
|
||||
browserCanvasContext.lineTo(pointB1.x, pointB1.y)
|
||||
browserCanvasContext.lineTo(pointB4.x, pointB4.y)
|
||||
browserCanvasContext.lineTo(pointA4.x, pointA4.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
drawEdge('left')
|
||||
|
||||
function drawEdge (edgeType) {
|
||||
if (whereIsMouseOver === edgeType && isMouseOver === true) {
|
||||
browserCanvasContext.lineWidth = lineWidth
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')'
|
||||
} else {
|
||||
browserCanvasContext.lineWidth = lineWidth
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')'
|
||||
}
|
||||
browserCanvasContext.fill()
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,309 @@
|
||||
function newPlottersManager () {
|
||||
const MODULE_NAME = 'Plotters Manager'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let timeFrame = INITIAL_TIME_PERIOD
|
||||
let datetime = NEW_SESSION_INITIAL_DATE
|
||||
|
||||
let thisObject = {
|
||||
fitFunction: undefined,
|
||||
container: undefined,
|
||||
payload: undefined,
|
||||
connectors: [],
|
||||
setDatetime: setDatetime,
|
||||
setTimeFrame: setTimeFrame,
|
||||
setCoordinateSystem: setCoordinateSystem,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.connectors = []
|
||||
|
||||
let initializationReady = false
|
||||
let layersPanel
|
||||
let onLayerStatusChangedEventSuscriptionId
|
||||
let coordinateSystem
|
||||
|
||||
setupContainer()
|
||||
return thisObject
|
||||
|
||||
function setupContainer () {
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
}
|
||||
|
||||
function finalize () {
|
||||
layersPanel.container.eventHandler.stopListening(onLayerStatusChangedEventSuscriptionId)
|
||||
layersPanel = undefined
|
||||
|
||||
for (let i = 0; i < thisObject.connectors.length; i++) {
|
||||
let connector = thisObject.connectors[i]
|
||||
/* Then the panels. */
|
||||
for (let j = 0; j < connector.panels.length; j++) {
|
||||
canvas.panelsSpace.destroyPanel(connector.panels[j])
|
||||
connector.panels[j] = undefined
|
||||
}
|
||||
connector.panels = undefined
|
||||
|
||||
/* Finalize the plotters */
|
||||
if (connector.plotter.finalize !== undefined) {
|
||||
connector.plotter.container.eventHandler.stopListening(connector.plotter.onRecordChangeEventsSubscriptionId)
|
||||
connector.plotter.finalize()
|
||||
}
|
||||
connector.plotter = undefined
|
||||
|
||||
/* Finally the Storage Objects */
|
||||
finalizeStorage(connector.storage)
|
||||
connector.storage = undefined
|
||||
|
||||
connector = undefined
|
||||
}
|
||||
thisObject.connectors = []
|
||||
coordinateSystem = undefined
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.payload = undefined
|
||||
}
|
||||
|
||||
function finalizeStorage (storage) {
|
||||
storage.eventHandler.stopListening(storage.onMarketFileLoadedLayerEventsSubscriptionId)
|
||||
storage.eventHandler.stopListening(storage.onDailyFileLoadedLayerEventsSubscriptionId)
|
||||
storage.eventHandler.stopListening(storage.onSingleFileLoadedLayerEventsSubscriptionId)
|
||||
storage.eventHandler.stopListening(storage.onFileSequenceLoadedLayerEventsSubscriptionId)
|
||||
storage.eventHandler.stopListening(storage.onDailyFileLoadedPlotterEventsSubscriptionId)
|
||||
storage.finalize()
|
||||
}
|
||||
|
||||
function initialize (pLayersPanel) {
|
||||
/* Remember the Layers Panel */
|
||||
layersPanel = pLayersPanel
|
||||
|
||||
/* Listen to the event of change of status */
|
||||
onLayerStatusChangedEventSuscriptionId = layersPanel.container.eventHandler.listenToEvent('Layer Status Changed', onLayerStatusChanged)
|
||||
}
|
||||
|
||||
function initializePlotter (layer) {
|
||||
try {
|
||||
/* Before Initializing a Plotter, we need the Storage it will use, loaded with the files it will initially need. */
|
||||
let storage = newProductStorage(layer.payload.node.id)
|
||||
|
||||
/*
|
||||
Before Initializing the Storage, we will put the Layer 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 < layer.definition.referenceParent.referenceParent.datasets.length; i++) {
|
||||
let dataset = layer.definition.referenceParent.referenceParent.datasets[i]
|
||||
|
||||
switch (dataset.code.type) {
|
||||
case 'Market Files': {
|
||||
storage.onMarketFileLoadedLayerEventsSubscriptionId = storage.eventHandler.listenToEvent('Market File Loaded', layer.onMarketFileLoaded)
|
||||
}
|
||||
break
|
||||
case 'Daily Files': {
|
||||
storage.onDailyFileLoadedLayerEventsSubscriptionId = storage.eventHandler.listenToEvent('Daily File Loaded', layer.onDailyFileLoaded)
|
||||
}
|
||||
break
|
||||
case 'Single File': {
|
||||
storage.onSingleFileLoadedLayerEventsSubscriptionId = storage.eventHandler.listenToEvent('Single File Loaded', layer.onSingleFileLoaded)
|
||||
}
|
||||
break
|
||||
case 'File Sequence': {
|
||||
storage.onFileSequenceLoadedLayerEventsSubscriptionId = storage.eventHandler.listenToEvent('File Sequence Loaded', layer.onFileSequenceLoaded)
|
||||
}
|
||||
break
|
||||
default: {
|
||||
if (ERROR_LOG === true) {
|
||||
logger.write('[ERROR] initializePlotter -> Dataset Type Incorrect or Undefined -> dataset.type = ' + dataset.type)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let baseAsset = layer.definition.referenceParent.parentNode.referenceParent.baseAsset.referenceParent.code.codeName
|
||||
let quotedAsset = layer.definition.referenceParent.parentNode.referenceParent.quotedAsset.referenceParent.code.codeName
|
||||
let market = {
|
||||
baseAsset: baseAsset,
|
||||
quotedAsset: quotedAsset
|
||||
}
|
||||
let product = layer.definition.referenceParent.referenceParent
|
||||
let bot = layer.definition.referenceParent.referenceParent.parentNode
|
||||
let dataMine = layer.definition.referenceParent.referenceParent.parentNode.parentNode
|
||||
let exchange = layer.definition.referenceParent.parentNode.referenceParent.parentNode.parentNode
|
||||
let plotterModule = layer.definition.referenceParent.referenceParent.referenceParent
|
||||
let session
|
||||
|
||||
if (layer.definition.referenceParent.parentNode.parentNode !== undefined) {
|
||||
if (layer.definition.referenceParent.parentNode.parentNode.type === 'Session Reference') {
|
||||
if (layer.definition.referenceParent.parentNode.parentNode.referenceParent !== undefined) {
|
||||
session = layer.definition.referenceParent.parentNode.parentNode.referenceParent
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
storage.initialize(
|
||||
dataMine,
|
||||
bot,
|
||||
session,
|
||||
product,
|
||||
exchange,
|
||||
market,
|
||||
datetime,
|
||||
timeFrame,
|
||||
onProductStorageInitialized
|
||||
)
|
||||
|
||||
function onProductStorageInitialized (err) {
|
||||
try {
|
||||
if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) {
|
||||
/* Now we have all the initial data loaded and ready to be delivered to the new instance of the plotter. */
|
||||
let plotter
|
||||
|
||||
if (plotterModule.code.isLegacy !== true) {
|
||||
plotter = newPlotter()
|
||||
} else {
|
||||
plotter = getNewPlotter(dataMine.code.codeName, plotterModule.parentNode.code.codeName, plotterModule.code.codeName)
|
||||
}
|
||||
|
||||
plotter.container.connectToParent(thisObject.container, true, true, false, true, true, true, false, false, 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, datetime, timeFrame, coordinateSystem, onPlotterInizialized, product)
|
||||
|
||||
function onPlotterInizialized () {
|
||||
try {
|
||||
let connector = {
|
||||
layer: layer,
|
||||
plotter: plotter,
|
||||
storage: storage
|
||||
}
|
||||
/* Let the Plotter listen to the event of Cursor Files loaded, so that it can react recalculating if needed. */
|
||||
plotter.onDailyFileLoadedPlotterEventsSubscriptionId = storage.eventHandler.listenToEvent('Daily File Loaded', plotter.onDailyFileLoaded)
|
||||
/* Lets load now this plotter panels. */
|
||||
connector.panels = []
|
||||
|
||||
if (product !== undefined) {
|
||||
for (let i = 0; i < plotterModule.panels.length; i++) {
|
||||
let panel = plotterModule.panels[i]
|
||||
|
||||
let parameters = {
|
||||
dataMine: dataMine.code.codeName,
|
||||
plotterCodeName: plotterModule.parentNode.code.codeName,
|
||||
moduleCodeName: plotterModule.code.codeName,
|
||||
panelNode: panel
|
||||
}
|
||||
|
||||
let owner = thisObject.payload.node.payload.parentNode.id // Panels are owned by the time machine.
|
||||
let plotterPanelHandle = canvas.panelsSpace.createNewPanel('Plotter Panel', parameters, owner, layer.session)
|
||||
let plotterPanel = canvas.panelsSpace.getPanel(plotterPanelHandle)
|
||||
|
||||
/* Connect Panel to the Plotter via an Event. */
|
||||
connector.plotter.onRecordChangeEventsSubscriptionId = connector.plotter.container.eventHandler.listenToEvent('Current Record Changed', plotterPanel.onRecordChange)
|
||||
connector.panels.push(plotterPanelHandle)
|
||||
}
|
||||
|
||||
connector.layer.panels = connector.panels
|
||||
}
|
||||
|
||||
thisObject.connectors.push(connector)
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initializePlotter -> onProductStorageInitialized -> onPlotterInizialized -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initializePlotter -> onProductStorageInitialized -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initializePlotter -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onLayerStatusChanged (layer) {
|
||||
if (layer.status === LAYER_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 < thisObject.connectors.length; i++) {
|
||||
let connector = thisObject.connectors[i]
|
||||
if (connector.layer.payload.node.id === layer.payload.node.id) {
|
||||
found = true
|
||||
}
|
||||
}
|
||||
if (found === false) {
|
||||
initializePlotter(layer, onProductPlotterInitialized)
|
||||
function onProductPlotterInitialized (err) {
|
||||
/* There is no policy yet of what to do if this fails. */
|
||||
}
|
||||
}
|
||||
}
|
||||
if (layer.status === LAYER_STATUS.OFF) {
|
||||
/* If the plotter of this card is not on our Active Plotters list, then we remove it. */
|
||||
for (let i = 0; i < thisObject.connectors.length; i++) {
|
||||
let connector = thisObject.connectors[i]
|
||||
if (connector.layer.payload.node.id === layer.payload.node.id) {
|
||||
/* Then the panels. */
|
||||
for (let j = 0; j < connector.panels.length; j++) {
|
||||
canvas.panelsSpace.destroyPanel(connector.panels[j])
|
||||
}
|
||||
/* Finally the Storage Objects */
|
||||
|
||||
if (connector.plotter.finalize !== undefined) {
|
||||
connector.plotter.container.eventHandler.stopListening(connector.plotter.onRecordChangeEventsSubscriptionId)
|
||||
connector.plotter.container.finalize()
|
||||
connector.plotter.finalize()
|
||||
}
|
||||
finalizeStorage(connector.storage)
|
||||
thisObject.connectors.splice(i, 1) // Delete item from array.
|
||||
return // We already found the product woth changes and processed it.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setTimeFrame (pTimeFrame) {
|
||||
timeFrame = pTimeFrame
|
||||
for (let i = 0; i < thisObject.connectors.length; i++) {
|
||||
let connector = thisObject.connectors[i]
|
||||
connector.layer.setTimeFrame(timeFrame)
|
||||
connector.storage.setTimeFrame(timeFrame)
|
||||
connector.plotter.setTimeFrame(timeFrame)
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime (pDatetime) {
|
||||
datetime = pDatetime
|
||||
for (let i = 0; i < thisObject.connectors.length; i++) {
|
||||
let connector = thisObject.connectors[i]
|
||||
connector.layer.setDatetime(pDatetime)
|
||||
connector.storage.setDatetime(pDatetime)
|
||||
connector.plotter.setDatetime(pDatetime)
|
||||
}
|
||||
}
|
||||
|
||||
function setCoordinateSystem (pCoordinateSystem) {
|
||||
coordinateSystem = pCoordinateSystem
|
||||
for (let i = 0; i < thisObject.connectors.length; i++) {
|
||||
let connector = thisObject.connectors[i]
|
||||
connector.plotter.setCoordinateSystem(coordinateSystem)
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.connectors === undefined) { return } // We need to wait
|
||||
/* First the Product Plotters. */
|
||||
for (let i = 0; i < thisObject.connectors.length; i++) {
|
||||
let connector = thisObject.connectors[thisObject.connectors.length - i - 1]
|
||||
connector.plotter.draw()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,629 @@
|
||||
/*
|
||||
|
||||
Markets are a function of time. When watching them, end users must be positioned at one particular point in time. The system currently allows users
|
||||
to position themselves at any time they like.
|
||||
|
||||
In the future, it will be usefull to explore markets and compare them at different times simultaneously. Anticipating that future this module exists.
|
||||
All the timelineCharts that depand on a datetime are children of this object Time Machine. In the future we will allow users to have more than one Time Machine,
|
||||
each one with it own timelineCharts, and each one positioned at an especific point in titme.
|
||||
|
||||
*/
|
||||
|
||||
function newTimeMachine () {
|
||||
const MODULE_NAME = 'Time Machine'
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let timeFrame = INITIAL_TIME_PERIOD
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
timeScale: undefined,
|
||||
rateScale: undefined,
|
||||
payload: undefined,
|
||||
edgeEditor: undefined,
|
||||
timelineCharts: [],
|
||||
fitFunction: fitFunction,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let timeMachineCoordinateSystem = newCoordinateSystem()
|
||||
|
||||
let mouse = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
|
||||
let drawScales = false
|
||||
let syncWithDesignerLoop = 0
|
||||
let timelineChartsMap = new Map()
|
||||
|
||||
let onViewportPositionChangedEventSuscriptionId
|
||||
let onViewportZoomChangedEventSuscriptionId
|
||||
let onMouseOverEventSuscriptionId
|
||||
let onMouseNotOverEventSuscriptionId
|
||||
let timeScaleMouseOverEventSuscriptionId
|
||||
let rateScaleMouseOverEventSuscriptionId
|
||||
let timeFrameScaleEventSuscriptionId
|
||||
let timeFrameScaleMouseOverEventSuscriptionId
|
||||
let onScaleChangedEventSubscriptionId
|
||||
|
||||
setupContainer()
|
||||
return thisObject
|
||||
|
||||
function setupContainer () {
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.fitFunction = thisObject.fitFunction
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.insideViewport = true
|
||||
thisObject.container.detectMouseOver = true
|
||||
|
||||
thisObject.container.frame.width = TIME_MACHINE_WIDTH
|
||||
thisObject.container.frame.height = TIME_MACHINE_HEIGHT
|
||||
}
|
||||
|
||||
function finalize () {
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(onViewportPositionChangedEventSuscriptionId)
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(onViewportZoomChangedEventSuscriptionId)
|
||||
timeMachineCoordinateSystem.eventHandler.stopListening(onScaleChangedEventSubscriptionId)
|
||||
|
||||
if (thisObject.timeScale !== undefined) {
|
||||
finalizeTimeScale()
|
||||
}
|
||||
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
finalizeRateScale()
|
||||
}
|
||||
|
||||
if (thisObject.timeFrameScale !== undefined) {
|
||||
finalizeTimeFrameScale()
|
||||
}
|
||||
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseNotOverEventSuscriptionId)
|
||||
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.timelineCharts[i]
|
||||
timelineChart.container.eventHandler.stopListening(timelineChart.onChildrenMouseOverEventSuscriptionId)
|
||||
timelineChart.finalize()
|
||||
}
|
||||
|
||||
thisObject.timelineCharts = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.edgeEditor = undefined
|
||||
|
||||
mouse = undefined
|
||||
timelineChartsMap = undefined
|
||||
|
||||
timeMachineCoordinateSystem.finalize()
|
||||
timeMachineCoordinateSystem = undefined
|
||||
}
|
||||
|
||||
function finalizeTimeScale () {
|
||||
if (thisObject.timeScale === undefined) { return }
|
||||
|
||||
thisObject.timeScale.container.eventHandler.stopListening(timeScaleMouseOverEventSuscriptionId)
|
||||
thisObject.timeScale.finalize()
|
||||
thisObject.timeScale = undefined
|
||||
}
|
||||
|
||||
function finalizeRateScale () {
|
||||
if (thisObject.rateScale === undefined) { return }
|
||||
|
||||
thisObject.rateScale.container.eventHandler.stopListening(rateScaleMouseOverEventSuscriptionId)
|
||||
thisObject.rateScale.finalize()
|
||||
thisObject.rateScale = undefined
|
||||
}
|
||||
|
||||
function finalizeTimeFrameScale () {
|
||||
if (thisObject.timeFrameScale === undefined) { return }
|
||||
|
||||
thisObject.timeFrameScale.container.eventHandler.stopListening(timeFrameScaleEventSuscriptionId)
|
||||
thisObject.timeFrameScale.container.eventHandler.stopListening(timeFrameScaleMouseOverEventSuscriptionId)
|
||||
thisObject.timeFrameScale.finalize()
|
||||
thisObject.timeFrameScale = undefined
|
||||
}
|
||||
|
||||
function initialize (callBackFunction) {
|
||||
timeFrame = INITIAL_TIME_PERIOD
|
||||
loadFrame(thisObject.payload, thisObject.container.frame)
|
||||
|
||||
// thisObject.container.frame.position.x = 0
|
||||
// thisObject.container.frame.position.y = 0
|
||||
|
||||
recalculateCoordinateSystem()
|
||||
recalculateCurrentDatetime()
|
||||
|
||||
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
onMouseNotOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
||||
onViewportPositionChangedEventSuscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Position Changed', onViewportPositionChanged)
|
||||
onViewportZoomChangedEventSuscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Zoom Changed', onViewportZoomChanged)
|
||||
onScaleChangedEventSubscriptionId = timeMachineCoordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
|
||||
thisObject.edgeEditor = newEdgeEditor()
|
||||
thisObject.edgeEditor.initialize(timeMachineCoordinateSystem)
|
||||
thisObject.edgeEditor.container.connectToParent(thisObject.container, true, true, false, true, true, true)
|
||||
|
||||
callBackFunction()
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
drawScales = true
|
||||
|
||||
mouse.position.x = event.x
|
||||
mouse.position.y = event.y
|
||||
|
||||
if (thisObject.timeScale !== undefined) {
|
||||
thisObject.timeScale.onMouseOverSomeTimeMachineContainer(event)
|
||||
}
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
thisObject.rateScale.onMouseOverSomeTimeMachineContainer(event)
|
||||
}
|
||||
if (thisObject.timeFrameScale !== undefined) {
|
||||
thisObject.timeFrameScale.onMouseOverSomeTimeMachineContainer(event)
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseNotOver (event) {
|
||||
drawScales = false
|
||||
|
||||
if (thisObject.timeScale !== undefined) {
|
||||
thisObject.timeScale.visible = false
|
||||
}
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
thisObject.rateScale.visible = false
|
||||
}
|
||||
if (thisObject.timeFrameScale !== undefined) {
|
||||
thisObject.timeFrameScale.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTimeScale () {
|
||||
thisObject.timeScale = newTimeScale()
|
||||
thisObject.timeScale.fitFunction = thisObject.fitFunction
|
||||
thisObject.timeScale.payload = thisObject.payload.node.timeScale.payload
|
||||
|
||||
timeScaleMouseOverEventSuscriptionId = thisObject.timeScale.container.eventHandler.listenToEvent('onMouseOverScale', timeScaleMouseOver)
|
||||
thisObject.timeScale.initialize(timeMachineCoordinateSystem, thisObject.container)
|
||||
|
||||
function timeScaleMouseOver (event) {
|
||||
thisObject.container.eventHandler.raiseEvent('onMouseOver', event)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeRateScale () {
|
||||
thisObject.rateScale = newRateScale()
|
||||
thisObject.rateScale.fitFunction = thisObject.fitFunction
|
||||
thisObject.rateScale.payload = thisObject.payload.node.rateScale.payload
|
||||
|
||||
rateScaleMouseOverEventSuscriptionId = thisObject.rateScale.container.eventHandler.listenToEvent('onMouseOverScale', rateScaleMouseOver)
|
||||
thisObject.rateScale.initialize(timeMachineCoordinateSystem, thisObject.container, thisObject.container)
|
||||
|
||||
function rateScaleMouseOver (event) {
|
||||
thisObject.container.eventHandler.raiseEvent('onMouseOver', event)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTimeFrameScale () {
|
||||
thisObject.timeFrameScale = newTimeFrameScale()
|
||||
thisObject.timeFrameScale.fitFunction = thisObject.fitFunction
|
||||
thisObject.timeFrameScale.payload = thisObject.payload.node.timeFrameScale.payload
|
||||
|
||||
timeFrameScaleEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('Time Frame Value Changed', timeFrameScaleValueChanged)
|
||||
timeFrameScaleMouseOverEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('onMouseOverScale', timeFrameScaleMouseOver)
|
||||
thisObject.timeFrameScale.initialize(thisObject.container)
|
||||
|
||||
function timeFrameScaleValueChanged (event) {
|
||||
let currentTimeFrame = timeFrame
|
||||
timeFrame = event.timeFrame
|
||||
if (timeFrame !== currentTimeFrame) {
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.timelineCharts[i]
|
||||
timelineChart.upstreamTimeFrame = timeFrame
|
||||
if (timelineChart.timeFrameScale === undefined) {
|
||||
timelineChart.setTimeFrame(timeFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function timeFrameScaleMouseOver (event) {
|
||||
thisObject.container.eventHandler.raiseEvent('onMouseOver', event)
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point, purpose) {
|
||||
let container
|
||||
|
||||
if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) {
|
||||
container = thisObject.rateScale.getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
if (container.isForThisPurpose(purpose)) {
|
||||
return container
|
||||
} else {
|
||||
if (purpose === GET_CONTAINER_PURPOSE.DRAGGING && container.isClickeable === true) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thisObject.timeScale !== undefined && thisObject.timeScale.isVisible === true) {
|
||||
container = thisObject.timeScale.getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
if (container.isForThisPurpose(purpose)) {
|
||||
return container
|
||||
} else {
|
||||
if (purpose === GET_CONTAINER_PURPOSE.DRAGGING && container.isClickeable === true) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) {
|
||||
container = thisObject.timeFrameScale.getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
if (container.isForThisPurpose(purpose)) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
container = thisObject.timelineCharts[i].getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
if (container.isForThisPurpose(purpose)) {
|
||||
if (thisObject.container.frame.isThisPointHere(point) === true) {
|
||||
return container
|
||||
}
|
||||
} else {
|
||||
if (purpose === GET_CONTAINER_PURPOSE.DRAGGING && container.isClickeable === true) {
|
||||
if (thisObject.container.frame.isThisPointHere(point) === true) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
container = thisObject.edgeEditor.getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
return container
|
||||
} else {
|
||||
if (thisObject.container.isForThisPurpose(purpose)) {
|
||||
if (thisObject.container.frame.isThisPointHere(point) === true) {
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onViewportZoomChanged (event) {
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
recalculateCurrentDatetime()
|
||||
}
|
||||
}
|
||||
|
||||
function onViewportPositionChanged () {
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
recalculateCurrentDatetime()
|
||||
}
|
||||
}
|
||||
|
||||
function onScaleChanged () {
|
||||
recalculateCurrentDatetime()
|
||||
}
|
||||
|
||||
function recalculateCurrentDatetime () {
|
||||
/*
|
||||
The view port was moved or the view port zoom level was changed and the center of the screen points to a different datetime that we
|
||||
must calculate.
|
||||
*/
|
||||
let center = {
|
||||
x: (canvas.chartingSpace.viewport.visibleArea.bottomRight.x - canvas.chartingSpace.viewport.visibleArea.bottomLeft.x) / 2,
|
||||
y: (canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topRight.y) / 2
|
||||
}
|
||||
|
||||
center = unTransformThisPoint(center, thisObject.container)
|
||||
center = timeMachineCoordinateSystem.unInverseTransform(center, thisObject.container.frame.height)
|
||||
|
||||
datetime = new Date(center.x)
|
||||
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.timelineCharts[i]
|
||||
timelineChart.setDatetime(datetime)
|
||||
}
|
||||
}
|
||||
|
||||
function fitFunction (point, fullVisible, margin, topMargin, bottomMargin) {
|
||||
/* We prevent a point to be out of the container AND out of the Charting Space in general */
|
||||
|
||||
let returnPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
let upCorner = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let bottonCorner = {
|
||||
x: thisObject.container.frame.width,
|
||||
y: thisObject.container.frame.height
|
||||
}
|
||||
|
||||
upCorner = transformThisPoint(upCorner, thisObject.container)
|
||||
bottonCorner = transformThisPoint(bottonCorner, thisObject.container)
|
||||
|
||||
/* Checking against the container limits. */
|
||||
if (margin === undefined) { margin = 0 }
|
||||
if (topMargin === undefined) { topMargin = 0 }
|
||||
if (bottomMargin === undefined) { bottomMargin = 0 }
|
||||
|
||||
if (returnPoint.x - margin < upCorner.x) { returnPoint.x = upCorner.x + margin }
|
||||
if (returnPoint.x + margin > bottonCorner.x) { returnPoint.x = bottonCorner.x - margin }
|
||||
if (returnPoint.y - margin - topMargin < upCorner.y) { returnPoint.y = upCorner.y + margin + topMargin }
|
||||
if (returnPoint.y + margin + bottomMargin > bottonCorner.y) { returnPoint.y = bottonCorner.y - margin - bottomMargin }
|
||||
|
||||
returnPoint = canvas.chartingSpace.fitFunction(returnPoint, fullVisible)
|
||||
|
||||
return returnPoint
|
||||
}
|
||||
|
||||
function physics () {
|
||||
thisObject.edgeEditor.physics()
|
||||
timeMachineCoordinateSystem.physics()
|
||||
|
||||
saveFrame(thisObject.payload, thisObject.container.frame)
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
childrenPhysics()
|
||||
syncWithDesigner()
|
||||
}
|
||||
panelPhysics()
|
||||
}
|
||||
|
||||
function panelPhysics () {
|
||||
if (thisObject.container.frame.isInViewPort() && canvas.chartingSpace.viewport.zoomTargetLevel > ZOOM_OUT_THRESHOLD_FOR_HIDDING_PANELS) {
|
||||
canvas.panelsSpace.unHide(thisObject.payload.node.id, 'Layers Panel')
|
||||
canvas.panelsSpace.unHide(thisObject.payload.node.id, 'Plotter Panel')
|
||||
} else {
|
||||
canvas.panelsSpace.hide(thisObject.payload.node.id, 'Layers Panel')
|
||||
canvas.panelsSpace.hide(thisObject.payload.node.id, 'Plotter Panel')
|
||||
}
|
||||
}
|
||||
|
||||
function syncWithDesigner () {
|
||||
syncWithDesignerTimelineCharts()
|
||||
syncWithDesignerScales()
|
||||
}
|
||||
|
||||
function syncWithDesignerScales () {
|
||||
if (thisObject.payload.node === undefined) {
|
||||
finalizeTimeScale()
|
||||
finalizeRateScale()
|
||||
finalizeTimeFrameScale()
|
||||
return
|
||||
}
|
||||
if (thisObject.payload.node.timeScale === undefined && thisObject.timeScale !== undefined) {
|
||||
finalizeTimeScale()
|
||||
}
|
||||
if (thisObject.payload.node.timeScale !== undefined && thisObject.timeScale === undefined) {
|
||||
initializeTimeScale()
|
||||
}
|
||||
if (thisObject.payload.node.rateScale === undefined && thisObject.rateScale !== undefined) {
|
||||
finalizeRateScale()
|
||||
}
|
||||
if (thisObject.payload.node.rateScale !== undefined && thisObject.rateScale === undefined) {
|
||||
initializeRateScale()
|
||||
}
|
||||
if (thisObject.payload.node.timeFrameScale === undefined && thisObject.timeFrameScale !== undefined) {
|
||||
finalizeTimeFrameScale()
|
||||
}
|
||||
if (thisObject.payload.node.timeFrameScale !== undefined && thisObject.timeFrameScale === undefined) {
|
||||
initializeTimeFrameScale()
|
||||
}
|
||||
}
|
||||
|
||||
function syncWithDesignerTimelineCharts () {
|
||||
syncWithDesignerLoop = syncWithDesignerLoop + 0.00000000001
|
||||
|
||||
for (let j = 0; j < thisObject.payload.node.timelineCharts.length; j++) {
|
||||
let node = thisObject.payload.node.timelineCharts[j]
|
||||
let timelineChart = timelineChartsMap.get(node.id)
|
||||
if (timelineChart === undefined) {
|
||||
/* The timeline timelineChart node is new, thus we need to initialize a new timelineChart */
|
||||
initializeTimelineChart(node, syncWithDesignerLoop)
|
||||
} else {
|
||||
/* The time machine already exists, we tag it as existing at the current loop. */
|
||||
timelineChart.syncWithDesignerLoop = syncWithDesignerLoop
|
||||
}
|
||||
}
|
||||
|
||||
/* We check all the timeMachines we have to see if we need to remove any of them */
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.timelineCharts[i]
|
||||
if (timelineChart.syncWithDesignerLoop < syncWithDesignerLoop) {
|
||||
/* Must be removed */
|
||||
timelineChart.container.eventHandler.stopListening(timelineChart.onChildrenMouseOverEventSuscriptionId)
|
||||
timelineChart.finalize()
|
||||
timelineChartsMap.delete(timelineChart.nodeId)
|
||||
thisObject.timelineCharts.splice(i, 1)
|
||||
/* We remove one at the time */
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTimelineChart (node, syncWithDesignerLoop) {
|
||||
let timelineChart = newTimelineChart()
|
||||
timelineChart.syncWithDesignerLoop = syncWithDesignerLoop
|
||||
timelineChart.payload = node.payload
|
||||
timelineChart.nodeId = node.id
|
||||
timelineChartsMap.set(node.id, timelineChart)
|
||||
timelineChart.payload.uiObject.setValue('Loading...')
|
||||
|
||||
/* Setting up the new timeline timelineChart. */
|
||||
timelineChart.container.connectToParent(thisObject.container, true, true, false, true, true, true, false, false, true)
|
||||
timelineChart.container.fitFunction = thisObject.container.fitFunction
|
||||
timelineChart.fitFunction = thisObject.fitFunction
|
||||
timelineChart.container.frame.width = thisObject.container.frame.width
|
||||
timelineChart.container.frame.height = thisObject.container.frame.height
|
||||
timelineChart.container.frame.position.x = 0
|
||||
timelineChart.container.frame.position.y = 0
|
||||
timelineChart.initialize(timeMachineCoordinateSystem, timeFrame)
|
||||
|
||||
/* we will store the event suscription id as a property of the timelineChart, to avoid keeping it an a separate array */
|
||||
timelineChart.onChildrenMouseOverEventSuscriptionId = timelineChart.container.eventHandler.listenToEvent('onChildrenMouseOver', onChildrenMouseOver)
|
||||
|
||||
thisObject.timelineCharts.push(timelineChart)
|
||||
timelineChart.payload.uiObject.setValue('')
|
||||
|
||||
function onChildrenMouseOver (event) {
|
||||
thisObject.container.eventHandler.raiseEvent('onMouseOver', event)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function childrenPhysics () {
|
||||
if (thisObject.timeScale !== undefined) {
|
||||
thisObject.timeScale.physics()
|
||||
}
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
thisObject.rateScale.physics()
|
||||
}
|
||||
if (thisObject.timeFrameScale !== undefined) {
|
||||
thisObject.timeFrameScale.physics()
|
||||
}
|
||||
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.timelineCharts[i]
|
||||
timelineChart.physics()
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
drawChartsBackground()
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.timelineCharts[thisObject.timelineCharts.length - i - 1]
|
||||
timelineChart.drawBackground()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawChartsBackground () {
|
||||
drawContainerBackground(thisObject.container, UI_COLOR.WHITE, 0.5, thisObject.fitFunction)
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.payload === undefined) { return }
|
||||
if (thisObject.payload.node === undefined) { return }
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.timelineCharts[thisObject.timelineCharts.length - i - 1]
|
||||
timelineChart.draw()
|
||||
}
|
||||
|
||||
if (drawScales === true) {
|
||||
if (thisObject.timeScale !== undefined && thisObject.timeScale.isVisible === true) { thisObject.timeScale.draw() }
|
||||
if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { thisObject.timeFrameScale.draw() }
|
||||
if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { thisObject.rateScale.draw() }
|
||||
}
|
||||
} else {
|
||||
let icon = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type)
|
||||
if (icon !== undefined) {
|
||||
if (icon.canDrawIcon === true) {
|
||||
let imageSize = 40
|
||||
let imagePosition = {
|
||||
x: thisObject.container.frame.width / 2,
|
||||
y: thisObject.container.frame.height / 2
|
||||
}
|
||||
|
||||
imagePosition = transformThisPoint(imagePosition, thisObject.container)
|
||||
imagePosition = thisObject.fitFunction(imagePosition, true)
|
||||
|
||||
browserCanvasContext.drawImage(
|
||||
icon,
|
||||
imagePosition.x - imageSize / 2,
|
||||
imagePosition.y - imageSize / 2,
|
||||
imageSize,
|
||||
imageSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.timelineCharts[thisObject.timelineCharts.length - i - 1]
|
||||
timelineChart.drawForeground()
|
||||
}
|
||||
|
||||
if (drawScales === true) {
|
||||
if (thisObject.timeScale !== undefined && thisObject.timeScale.isVisible === true) { thisObject.timeScale.drawForeground() }
|
||||
if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { thisObject.timeFrameScale.drawForeground() }
|
||||
if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { thisObject.rateScale.drawForeground() }
|
||||
}
|
||||
|
||||
drawLabel()
|
||||
thisObject.edgeEditor.drawForeground()
|
||||
}
|
||||
}
|
||||
|
||||
function drawLabel () {
|
||||
/* Draw Title Above the Container */
|
||||
let position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
let imageSize = 25
|
||||
let fontSize = 25
|
||||
let opacity = 1
|
||||
let icon = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type)
|
||||
|
||||
position = transformThisPoint(position, thisObject.container)
|
||||
printLabel(thisObject.payload.node.name, position.x + 30, position.y - 10, opacity, fontSize)
|
||||
|
||||
if (icon !== undefined) {
|
||||
if (icon.canDrawIcon === true) {
|
||||
browserCanvasContext.drawImage(
|
||||
icon, position.x - 0,
|
||||
position.y - 30,
|
||||
imageSize,
|
||||
imageSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateCoordinateSystem () {
|
||||
let minValue = {
|
||||
x: MIN_PLOTABLE_DATE.valueOf(),
|
||||
y: 0
|
||||
}
|
||||
|
||||
let maxValue = {
|
||||
x: MAX_PLOTABLE_DATE.valueOf(),
|
||||
y: nextPorwerOf10(MAX_DEFAULT_RATE_SCALE_VALUE) / 4
|
||||
}
|
||||
|
||||
timeMachineCoordinateSystem.initialize(
|
||||
minValue,
|
||||
maxValue,
|
||||
thisObject.container.frame.width,
|
||||
thisObject.container.frame.height
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,407 @@
|
||||
function newTimelineChart () {
|
||||
const MODULE_NAME = 'Timeline Chart'
|
||||
const INFO_LOG = false
|
||||
const INTENSIVE_LOG = false
|
||||
const ERROR_LOG = true
|
||||
let logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let timeFrame = INITIAL_TIME_PERIOD
|
||||
let datetime = NEW_SESSION_INITIAL_DATE
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
rateScale: undefined,
|
||||
timeFrameScale: undefined,
|
||||
payload: undefined,
|
||||
layersManager: undefined,
|
||||
plotterManager: undefined,
|
||||
upstreamTimeFrame: undefined,
|
||||
setDatetime: setDatetime,
|
||||
setTimeFrame: setTimeFrame,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let timeMachineCoordinateSystem
|
||||
let timelineChartCoordinateSystem = newCoordinateSystem()
|
||||
let coordinateSystem
|
||||
|
||||
let layersPanel
|
||||
let layersPanelHandle
|
||||
|
||||
let onMouseOverEventSuscriptionId
|
||||
let onMouseNotOverEventSuscriptionId
|
||||
let rateScaleUpstreamEventSuscriptionId
|
||||
let rateScaleMouseOverEventSuscriptionId
|
||||
let timeFrameScaleEventSuscriptionId
|
||||
let timeFrameScaleMouseOverEventSuscriptionId
|
||||
let scaleChangedEventSubscriptionId
|
||||
|
||||
let drawScales = false
|
||||
let mouse = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
|
||||
setupContainer()
|
||||
return thisObject
|
||||
|
||||
function setupContainer () {
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.detectMouseOver = true
|
||||
}
|
||||
|
||||
function finalize () {
|
||||
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
|
||||
|
||||
if (thisObject.layersManager !== undefined) {
|
||||
finalizeLayersManager()
|
||||
}
|
||||
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
finalizeRateScale()
|
||||
}
|
||||
|
||||
if (thisObject.timeFrameScale !== undefined) {
|
||||
finalizeTimeFrameScale()
|
||||
}
|
||||
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseNotOverEventSuscriptionId)
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.payload = undefined
|
||||
|
||||
mouse = undefined
|
||||
logger = undefined
|
||||
|
||||
timeMachineCoordinateSystem = undefined
|
||||
timelineChartCoordinateSystem.finalize()
|
||||
timelineChartCoordinateSystem = undefined
|
||||
coordinateSystem = undefined
|
||||
layersPanel = undefined
|
||||
layersPanelHandle = undefined
|
||||
}
|
||||
|
||||
function finalizeLayersManager () {
|
||||
if (thisObject.plotterManager !== undefined) {
|
||||
thisObject.plotterManager.finalize()
|
||||
}
|
||||
thisObject.plotterManager = undefined
|
||||
thisObject.layersManager = undefined
|
||||
|
||||
canvas.panelsSpace.destroyPanel(layersPanelHandle)
|
||||
}
|
||||
|
||||
function finalizeTimeFrameScale () {
|
||||
if (thisObject.timeFrameScale === undefined) { return }
|
||||
|
||||
thisObject.timeFrameScale.container.eventHandler.stopListening(timeFrameScaleEventSuscriptionId)
|
||||
thisObject.timeFrameScale.container.eventHandler.stopListening(timeFrameScaleMouseOverEventSuscriptionId)
|
||||
thisObject.timeFrameScale.finalize()
|
||||
thisObject.timeFrameScale = undefined
|
||||
if (thisObject.upstreamTimeFrame !== undefined) {
|
||||
timeFrame = thisObject.upstreamTimeFrame
|
||||
if (thisObject.plotterManager !== undefined) {
|
||||
thisObject.plotterManager.setTimeFrame(timeFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function finalizeRateScale () {
|
||||
if (thisObject.rateScale === undefined) { return }
|
||||
|
||||
thisObject.rateScale.container.eventHandler.stopListening(rateScaleMouseOverEventSuscriptionId)
|
||||
thisObject.container.parentContainer.eventHandler.stopListening(rateScaleUpstreamEventSuscriptionId)
|
||||
thisObject.rateScale.finalize()
|
||||
thisObject.rateScale = undefined
|
||||
|
||||
/* Resets the local container with the dimessions of its parent, the Time Machine */
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
thisObject.container.frame.height = thisObject.container.parentContainer.frame.height
|
||||
thisObject.container.frame.width = thisObject.container.parentContainer.frame.width
|
||||
}
|
||||
|
||||
function initialize (pTimeMachineCoordinateSystem, pTimeFrame) {
|
||||
/* We load the logow we will need for the background. */
|
||||
|
||||
timeMachineCoordinateSystem = pTimeMachineCoordinateSystem
|
||||
coordinateSystem = timeMachineCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
|
||||
timeFrame = pTimeFrame
|
||||
|
||||
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
onMouseNotOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
||||
scaleChangedEventSubscriptionId = timeMachineCoordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
}
|
||||
|
||||
function initializeCoordinateSystem () {
|
||||
let minValue = {
|
||||
x: timeMachineCoordinateSystem.min.x,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let maxValue = {
|
||||
x: timeMachineCoordinateSystem.max.x,
|
||||
y: nextPorwerOf10(MAX_DEFAULT_RATE_SCALE_VALUE) / 4
|
||||
}
|
||||
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
minValue.y = thisObject.rateScale.minValue
|
||||
maxValue.y = thisObject.rateScale.maxValue
|
||||
}
|
||||
|
||||
timelineChartCoordinateSystem.initialize(
|
||||
minValue,
|
||||
maxValue,
|
||||
thisObject.container.frame.width,
|
||||
thisObject.container.frame.height
|
||||
)
|
||||
}
|
||||
|
||||
function initializeLayersManager () {
|
||||
let owner = thisObject.payload.node.payload.parentNode.id // The real owner is the Time Machine
|
||||
layersPanelHandle = canvas.panelsSpace.createNewPanel('Layers Panel', undefined, owner)
|
||||
thisObject.layersManager = canvas.panelsSpace.getPanel(layersPanelHandle)
|
||||
thisObject.layersManager.payload = thisObject.payload.node.layersManager.payload
|
||||
thisObject.layersManager.initialize()
|
||||
|
||||
/* Initialize the Plotter Manager */
|
||||
thisObject.plotterManager = newPlottersManager()
|
||||
|
||||
thisObject.plotterManager.container.connectToParent(thisObject.container, true, true, false, true, true, true, false, false, true)
|
||||
|
||||
thisObject.plotterManager.container.frame.position.x = 0
|
||||
thisObject.plotterManager.container.frame.position.y = 0
|
||||
|
||||
thisObject.plotterManager.fitFunction = thisObject.fitFunction
|
||||
thisObject.plotterManager.payload = thisObject.payload
|
||||
thisObject.plotterManager.initialize(thisObject.layersManager)
|
||||
thisObject.plotterManager.setTimeFrame(timeFrame)
|
||||
thisObject.plotterManager.setDatetime(datetime)
|
||||
thisObject.plotterManager.setCoordinateSystem(coordinateSystem)
|
||||
}
|
||||
|
||||
function initializeRateScale () {
|
||||
thisObject.rateScale = newRateScale()
|
||||
thisObject.rateScale.fitFunction = thisObject.fitFunction
|
||||
thisObject.rateScale.payload = thisObject.payload.node.rateScale.payload
|
||||
|
||||
rateScaleMouseOverEventSuscriptionId = thisObject.rateScale.container.eventHandler.listenToEvent('onMouseOverScale', rateScaleMouseOver)
|
||||
thisObject.rateScale.initialize(timelineChartCoordinateSystem, thisObject.container.parentContainer, thisObject.container)
|
||||
|
||||
function rateScaleMouseOver (event) {
|
||||
thisObject.container.eventHandler.raiseEvent('onChildrenMouseOver', event)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTimeFrameScale () {
|
||||
thisObject.timeFrameScale = newTimeFrameScale()
|
||||
thisObject.timeFrameScale.fitFunction = thisObject.fitFunction
|
||||
thisObject.timeFrameScale.payload = thisObject.payload.node.timeFrameScale.payload
|
||||
|
||||
timeFrameScaleEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('Time Frame Value Changed', timeFrameScaleValueChanged)
|
||||
timeFrameScaleMouseOverEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('onMouseOverScale', timeFrameScaleMouseOver)
|
||||
thisObject.timeFrameScale.initialize(thisObject.container.parentContainer)
|
||||
|
||||
function timeFrameScaleValueChanged (event) {
|
||||
let currentTimeFrame = timeFrame
|
||||
timeFrame = event.timeFrame
|
||||
if (timeFrame !== currentTimeFrame) {
|
||||
if (thisObject.plotterManager !== undefined) {
|
||||
thisObject.plotterManager.setTimeFrame(timeFrame)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function timeFrameScaleMouseOver (event) {
|
||||
thisObject.container.eventHandler.raiseEvent('onChildrenMouseOver', event)
|
||||
}
|
||||
}
|
||||
|
||||
function onScaleChanged (event) {
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
thisObject.rateScale.onUpstreamScaleChanged(event)
|
||||
}
|
||||
timelineChartCoordinateSystem.min.x = timeMachineCoordinateSystem.min.x
|
||||
timelineChartCoordinateSystem.max.x = timeMachineCoordinateSystem.max.x
|
||||
timelineChartCoordinateSystem.maxHeight = timeMachineCoordinateSystem.maxHeight
|
||||
timelineChartCoordinateSystem.maxWidth = timeMachineCoordinateSystem.maxWidth
|
||||
timelineChartCoordinateSystem.autoMinXScale = timeMachineCoordinateSystem.autoMinXScale
|
||||
timelineChartCoordinateSystem.autoMaxXScale = timeMachineCoordinateSystem.autoMaxXScale
|
||||
timelineChartCoordinateSystem.recalculateScale()
|
||||
}
|
||||
|
||||
function setTimeFrame (pTimeFrame) {
|
||||
timeFrame = pTimeFrame
|
||||
if (thisObject.plotterManager !== undefined) {
|
||||
thisObject.plotterManager.setTimeFrame(timeFrame)
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime (pDatetime) {
|
||||
datetime = pDatetime
|
||||
if (thisObject.plotterManager !== undefined) {
|
||||
thisObject.plotterManager.setDatetime(datetime)
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
/* This event gets to the timelinechart container because it inherits it from the time machine container, which is the one raising Mouse Over and Mouse not Over Events to its children. */
|
||||
drawScales = true
|
||||
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
thisObject.rateScale.onMouseOverSomeTimeMachineContainer(event)
|
||||
}
|
||||
|
||||
if (thisObject.timeFrameScale !== undefined) {
|
||||
thisObject.timeFrameScale.onMouseOverSomeTimeMachineContainer(event)
|
||||
}
|
||||
|
||||
mouse.position.x = event.x
|
||||
mouse.position.y = event.y
|
||||
}
|
||||
|
||||
function onMouseNotOver (event) {
|
||||
/* This event is inherited from the Time Machine */
|
||||
drawScales = false
|
||||
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
thisObject.rateScale.visible = false
|
||||
}
|
||||
if (thisObject.timeFrameScale !== undefined) {
|
||||
thisObject.timeFrameScale.visible = false
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point, purpose) {
|
||||
let container
|
||||
|
||||
if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) {
|
||||
container = thisObject.rateScale.getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
if (container.isForThisPurpose(purpose)) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) {
|
||||
container = thisObject.timeFrameScale.getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
if (container.isForThisPurpose(purpose)) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
function physics () {
|
||||
thisObjectPhysics()
|
||||
childrenPhysics()
|
||||
syncWithDesignerScales()
|
||||
syncWithDesignerLayersManager()
|
||||
}
|
||||
|
||||
function thisObjectPhysics () {
|
||||
timelineChartCoordinateSystem.physics()
|
||||
}
|
||||
|
||||
function syncWithDesignerLayersManager () {
|
||||
if (thisObject.payload.node === undefined) {
|
||||
finalizeLayersManager()
|
||||
return
|
||||
}
|
||||
|
||||
if (thisObject.payload.node.layersManager === undefined && thisObject.layersManager !== undefined) {
|
||||
finalizeLayersManager()
|
||||
}
|
||||
if (thisObject.payload.node.layersManager !== undefined && thisObject.layersManager === undefined) {
|
||||
initializeLayersManager()
|
||||
}
|
||||
}
|
||||
|
||||
function syncWithDesignerScales () {
|
||||
if (thisObject.payload.node === undefined) {
|
||||
finalizeRateScale()
|
||||
finalizeTimeFrameScale()
|
||||
return
|
||||
}
|
||||
if (thisObject.payload.node.rateScale === undefined && thisObject.rateScale !== undefined) {
|
||||
finalizeRateScale()
|
||||
coordinateSystem = timeMachineCoordinateSystem
|
||||
if (thisObject.plotterManager !== undefined) {
|
||||
thisObject.plotterManager.setCoordinateSystem(coordinateSystem)
|
||||
}
|
||||
}
|
||||
if (thisObject.payload.node.rateScale !== undefined && thisObject.rateScale === undefined) {
|
||||
initializeRateScale()
|
||||
coordinateSystem = timelineChartCoordinateSystem
|
||||
if (thisObject.plotterManager !== undefined) {
|
||||
thisObject.plotterManager.setCoordinateSystem(coordinateSystem)
|
||||
}
|
||||
}
|
||||
if (thisObject.payload.node.timeFrameScale === undefined && thisObject.timeFrameScale !== undefined) {
|
||||
finalizeTimeFrameScale()
|
||||
}
|
||||
if (thisObject.payload.node.timeFrameScale !== undefined && thisObject.timeFrameScale === undefined) {
|
||||
initializeTimeFrameScale()
|
||||
}
|
||||
}
|
||||
|
||||
function childrenPhysics () {
|
||||
if (thisObject.rateScale !== undefined) {
|
||||
thisObject.rateScale.layersOn = thisObject.plotterManager.connectors.length
|
||||
thisObject.rateScale.physics()
|
||||
}
|
||||
if (thisObject.timeFrameScale !== undefined) {
|
||||
thisObject.timeFrameScale.layersOn = thisObject.plotterManager.connectors.length
|
||||
thisObject.timeFrameScale.physics()
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
drawChartsBackground()
|
||||
if (thisObject.plotterManager !== undefined) {
|
||||
thisObject.plotterManager.draw()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
if (drawScales === true) {
|
||||
if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { thisObject.timeFrameScale.draw() }
|
||||
if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { thisObject.rateScale.draw() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
if (thisObject.container.frame.isInViewPort()) {
|
||||
if (drawScales === true) {
|
||||
if (thisObject.timeFrameScale !== undefined && thisObject.timeFrameScale.isVisible === true) { thisObject.timeFrameScale.drawForeground() }
|
||||
if (thisObject.rateScale !== undefined && thisObject.rateScale.isVisible === true) { thisObject.rateScale.drawForeground() }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawChartsBackground () {
|
||||
drawContainerBackground(thisObject.container, UI_COLOR.WHITE, 0, thisObject.fitFunction)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,400 @@
|
||||
|
||||
function newViewport () {
|
||||
const MODULE_NAME = 'Viewport'
|
||||
|
||||
const MIN_ZOOM_LEVEL = 0
|
||||
const MAX_ZOOM_LEVEL = 500
|
||||
|
||||
let ANIMATION_INCREMENT = 0.25
|
||||
let ANIMATION_STEPS = 5
|
||||
|
||||
let TOP_MARGIN = 40 + TOP_SPACE_HEIGHT
|
||||
let BOTTOM_MARGIN = 42 + COCKPIT_SPACE_HEIGHT
|
||||
let LEFT_MARGIN = 0
|
||||
let RIGHT_MARGIN = 0
|
||||
let MARGINS = {
|
||||
TOP: TOP_MARGIN,
|
||||
BOTTOM: BOTTOM_MARGIN,
|
||||
LEFT: LEFT_MARGIN,
|
||||
RIGHT: RIGHT_MARGIN
|
||||
}
|
||||
|
||||
let thisObject = {
|
||||
visibleArea: undefined,
|
||||
width: undefined,
|
||||
height: undefined,
|
||||
eventHandler: undefined,
|
||||
zoomTargetLevel: undefined,
|
||||
zoomLevel: undefined,
|
||||
mousePosition: undefined,
|
||||
margins: MARGINS,
|
||||
payload: undefined,
|
||||
changeZoom: changeZoom,
|
||||
onMouseWheel: onMouseWheel,
|
||||
transformThisPoint: transformThisPoint,
|
||||
unTransformThisPoint: unTransformThisPoint,
|
||||
isThisPointVisible: isThisPointVisible,
|
||||
isThisPointInViewport: isThisPointInViewport,
|
||||
fitIntoVisibleArea: fitIntoVisibleArea,
|
||||
fitIntoViewport: fitIntoViewport,
|
||||
displace: displace,
|
||||
physics: physics,
|
||||
raiseEvents: raiseEvents,
|
||||
resize: resize,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
let increment = 0.035
|
||||
let position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
let targetPosition = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
let positionIncrement = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
thisObject.mousePosition = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
thisObject.eventHandler = newEventHandler()
|
||||
|
||||
/* Initial default value */
|
||||
thisObject.zoomLevel = MIN_ZOOM_LEVEL
|
||||
thisObject.zoomTargetLevel = MIN_ZOOM_LEVEL
|
||||
INITIAL_TIME_PERIOD = recalculatePeriod(thisObject.zoomLevel)
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.eventHandler.finalize()
|
||||
thisObject.payload = undefined
|
||||
thisObject = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
if (thisObject.payload !== undefined) {
|
||||
/* Read the position from the frame structure */
|
||||
|
||||
let frame = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
loadFrame(thisObject.payload, frame)
|
||||
if (!isNaN(frame.position.x)) {
|
||||
position.x = frame.position.x
|
||||
}
|
||||
if (!isNaN(frame.position.y)) {
|
||||
position.y = frame.position.y
|
||||
}
|
||||
}
|
||||
|
||||
resize()
|
||||
readObjectState()
|
||||
}
|
||||
|
||||
function resize () {
|
||||
TOP_MARGIN = 40 + TOP_SPACE_HEIGHT
|
||||
BOTTOM_MARGIN = 42 + browserCanvas.height - COCKPIT_SPACE_POSITION
|
||||
LEFT_MARGIN = 0
|
||||
RIGHT_MARGIN = 0
|
||||
MARGINS = {
|
||||
TOP: TOP_MARGIN,
|
||||
BOTTOM: BOTTOM_MARGIN,
|
||||
LEFT: LEFT_MARGIN,
|
||||
RIGHT: RIGHT_MARGIN
|
||||
}
|
||||
|
||||
thisObject.visibleArea = {
|
||||
topLeft: { x: LEFT_MARGIN, y: TOP_MARGIN },
|
||||
topRight: { x: browserCanvas.width - RIGHT_MARGIN, y: TOP_MARGIN },
|
||||
bottomRight: { x: browserCanvas.width - RIGHT_MARGIN, y: browserCanvas.height - BOTTOM_MARGIN},
|
||||
bottomLeft: { x: LEFT_MARGIN, y: browserCanvas.height - BOTTOM_MARGIN}
|
||||
}
|
||||
|
||||
thisObject.width = thisObject.visibleArea.topRight.x - thisObject.visibleArea.topLeft.x
|
||||
thisObject.height = thisObject.visibleArea.bottomRight.y - thisObject.visibleArea.topLeft.y
|
||||
|
||||
thisObject.center = {
|
||||
x: LEFT_MARGIN + thisObject.width / 2,
|
||||
y: TOP_MARGIN + thisObject.height / 2
|
||||
}
|
||||
}
|
||||
|
||||
function raiseEvents () {
|
||||
let event = {
|
||||
newPosition: position
|
||||
}
|
||||
|
||||
thisObject.eventHandler.raiseEvent('Position Changed', event)
|
||||
}
|
||||
|
||||
function physics () {
|
||||
animationPhysics()
|
||||
positioningPhysics()
|
||||
readObjectState()
|
||||
}
|
||||
|
||||
function positioningPhysics () {
|
||||
if (thisObject.payload === undefined) { return }
|
||||
/* Save the position at the frame lavel */
|
||||
let frame = {}
|
||||
frame.position = position
|
||||
saveFrame(thisObject.payload, frame)
|
||||
}
|
||||
|
||||
function animationPhysics () {
|
||||
if (thisObject.zoomLevel < thisObject.zoomTargetLevel) {
|
||||
if (thisObject.zoomTargetLevel - thisObject.zoomLevel < ANIMATION_INCREMENT) {
|
||||
ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel)
|
||||
}
|
||||
thisObject.zoomLevel = thisObject.zoomLevel + ANIMATION_INCREMENT
|
||||
changeZoom(thisObject.zoomLevel - ANIMATION_INCREMENT, thisObject.zoomLevel)
|
||||
}
|
||||
|
||||
if (thisObject.zoomLevel > thisObject.zoomTargetLevel) {
|
||||
if (thisObject.zoomLevel - thisObject.zoomTargetLevel < ANIMATION_INCREMENT) {
|
||||
ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel)
|
||||
}
|
||||
thisObject.zoomLevel = thisObject.zoomLevel - ANIMATION_INCREMENT
|
||||
changeZoom(thisObject.zoomLevel + ANIMATION_INCREMENT, thisObject.zoomLevel)
|
||||
}
|
||||
|
||||
if (positionIncrement.y !== 0) {
|
||||
if (Math.trunc(Math.abs(targetPosition.y - position.y) * 1000) >= Math.trunc(Math.abs(positionIncrement.y) * 1000)) {
|
||||
position.y = position.y + positionIncrement.y
|
||||
} else {
|
||||
positionIncrement.y = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseWheel (event) {
|
||||
if ((event.ctrlKey === true || event.metaKey === true)) { return }
|
||||
let morePower = 1
|
||||
let amount = event.delta
|
||||
if (event.buttons === 4) { morePower = 2 } // Mouse wheel pressed.
|
||||
/* We adjust the sensitivity for Mac Users */
|
||||
let isMac = navigator.platform.toUpperCase().indexOf('MAC') >= 0
|
||||
if (isMac) { amount = amount / 5 }
|
||||
|
||||
if (thisObject.zoomTargetLevel > 10) {
|
||||
amount = amount * 2
|
||||
}
|
||||
if (thisObject.zoomTargetLevel > 25) {
|
||||
amount = amount * 3
|
||||
}
|
||||
if (thisObject.zoomTargetLevel > 50) {
|
||||
amount = amount * 3.5
|
||||
}
|
||||
|
||||
if (thisObject.zoomTargetLevel + amount * morePower > MAX_ZOOM_LEVEL) {
|
||||
return false
|
||||
}
|
||||
if (thisObject.zoomTargetLevel + amount * morePower < MIN_ZOOM_LEVEL) {
|
||||
return false
|
||||
}
|
||||
thisObject.zoomTargetLevel = thisObject.zoomTargetLevel + amount * morePower
|
||||
|
||||
ANIMATION_INCREMENT = Math.abs(thisObject.zoomTargetLevel - thisObject.zoomLevel) / ANIMATION_STEPS
|
||||
|
||||
let newEvent = {
|
||||
newLevel: thisObject.zoomTargetLevel,
|
||||
newPosition: position,
|
||||
type: undefined,
|
||||
shiftKey: event.shiftKey
|
||||
}
|
||||
|
||||
if (amount > 0) {
|
||||
newEvent.type = 'Zoom In'
|
||||
} else {
|
||||
newEvent.type = 'Zoom Out'
|
||||
}
|
||||
|
||||
thisObject.eventHandler.raiseEvent('Zoom Changed', newEvent)
|
||||
return true
|
||||
}
|
||||
|
||||
function fitIntoVisibleArea (point) {
|
||||
let pointCopy = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
/* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */
|
||||
if (pointCopy.x > thisObject.visibleArea.bottomRight.x + 1) {
|
||||
pointCopy.x = thisObject.visibleArea.bottomRight.x + 1
|
||||
}
|
||||
|
||||
if (pointCopy.x < thisObject.visibleArea.topLeft.x - 1) {
|
||||
pointCopy.x = thisObject.visibleArea.topLeft.x - 1
|
||||
}
|
||||
|
||||
if (pointCopy.y > thisObject.visibleArea.bottomRight.y + 1) {
|
||||
pointCopy.y = thisObject.visibleArea.bottomRight.y + 1
|
||||
}
|
||||
|
||||
if (pointCopy.y < thisObject.visibleArea.topLeft.y - 1) {
|
||||
pointCopy.y = thisObject.visibleArea.topLeft.y - 1
|
||||
}
|
||||
|
||||
return pointCopy
|
||||
}
|
||||
|
||||
function fitIntoViewport (point) {
|
||||
let pointCopy = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
if (pointCopy.y > COCKPIT_SPACE_POSITION) {
|
||||
pointCopy.y = COCKPIT_SPACE_POSITION
|
||||
}
|
||||
return pointCopy
|
||||
}
|
||||
|
||||
function displace (displaceVector, recalculate) {
|
||||
position.x = position.x + displaceVector.x
|
||||
position.y = position.y + displaceVector.y
|
||||
|
||||
saveObjectState()
|
||||
|
||||
let event = {
|
||||
newPosition: position,
|
||||
recalculate: recalculate
|
||||
}
|
||||
|
||||
thisObject.eventHandler.raiseEvent('Position Changed', event)
|
||||
}
|
||||
|
||||
function changeZoom (oldLevel, newLevel) {
|
||||
let oldMouse = unTransformThisPoint(thisObject.mousePosition, oldLevel)
|
||||
let newMouse = transformThisPoint(oldMouse, newLevel)
|
||||
|
||||
position.x = position.x - newMouse.x + thisObject.mousePosition.x
|
||||
position.y = position.y - newMouse.y + thisObject.mousePosition.y
|
||||
|
||||
let testPoint = unTransformThisPoint(thisObject.mousePosition, newLevel)
|
||||
|
||||
saveObjectState()
|
||||
|
||||
targetPosition.x = position.x
|
||||
targetPosition.y = position.y
|
||||
|
||||
positionIncrement = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
thisObject.eventHandler.raiseEvent('Zoom Changed')
|
||||
}
|
||||
|
||||
function transformThisPoint (point, level) {
|
||||
let transformedPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
if (level === undefined) {
|
||||
transformedPoint.x = point.x * (1 + thisObject.zoomLevel) + position.x
|
||||
transformedPoint.y = point.y * (1 + thisObject.zoomLevel) + position.y
|
||||
} else {
|
||||
transformedPoint.x = point.x * (1 + level) + position.x
|
||||
transformedPoint.y = point.y * (1 + level) + position.y
|
||||
}
|
||||
|
||||
return transformedPoint
|
||||
}
|
||||
|
||||
function unTransformThisPoint (point, level) {
|
||||
let pointWithoutZoom = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
if (level === undefined) {
|
||||
pointWithoutZoom.x = (point.x - position.x) / (1 + thisObject.zoomLevel)
|
||||
pointWithoutZoom.y = (point.y - position.y) / (1 + thisObject.zoomLevel)
|
||||
} else {
|
||||
pointWithoutZoom.x = (point.x - position.x) / (1 + level)
|
||||
pointWithoutZoom.y = (point.y - position.y) / (1 + level)
|
||||
}
|
||||
|
||||
return pointWithoutZoom
|
||||
}
|
||||
|
||||
function isThisPointVisible (point) {
|
||||
if (point.x < thisObject.visibleArea.topLeft.x || point.x > thisObject.visibleArea.bottomRight.x || point.y < thisObject.visibleArea.topLeft.y || point.y > thisObject.visibleArea.bottomRight.y) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function isThisPointInViewport (point) {
|
||||
if (point.y > COCKPIT_SPACE_POSITION) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
function saveObjectState () {
|
||||
if (thisObject.payload === undefined) { return }
|
||||
|
||||
/* Save the zoom at the node config, so that the user can change it if he wishes to. */
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
code.zoom = (thisObject.zoomTargetLevel - MIN_ZOOM_LEVEL) / (MAX_ZOOM_LEVEL - MIN_ZOOM_LEVEL) * 100
|
||||
code.zoom = code.zoom.toFixed(2)
|
||||
thisObject.payload.node.code = JSON.stringify(code)
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function readObjectState () {
|
||||
if (thisObject.payload === undefined) { return }
|
||||
|
||||
/* Read the zoom level from the node config */
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
|
||||
if (isNaN(code.zoom) || code.zoom === null || code.zoom === undefined) {
|
||||
saveObjectState()
|
||||
return
|
||||
}
|
||||
code.zoom = code.zoom / 100 * (MAX_ZOOM_LEVEL - MIN_ZOOM_LEVEL) + MIN_ZOOM_LEVEL
|
||||
if (code.zoom < MIN_ZOOM_LEVEL) { code.zoom = MIN_ZOOM_LEVEL }
|
||||
if (code.zoom > MAX_ZOOM_LEVEL) { code.zoom = MAX_ZOOM_LEVEL }
|
||||
|
||||
if (code.zoom.toFixed(2) !== thisObject.zoomTargetLevel.toFixed(2)) {
|
||||
newZoomLevel(code.zoom)
|
||||
} else {
|
||||
saveObjectState()
|
||||
}
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
|
||||
function newZoomLevel (level) {
|
||||
thisObject.zoomLevel = level
|
||||
thisObject.zoomTargetLevel = level
|
||||
INITIAL_TIME_PERIOD = recalculatePeriod(level)
|
||||
saveObjectState()
|
||||
ANIMATION_INCREMENT = 0
|
||||
|
||||
let event = {
|
||||
newLevel: thisObject.zoomTargetLevel,
|
||||
newPosition: position,
|
||||
type: undefined
|
||||
}
|
||||
thisObject.eventHandler.raiseEvent('Zoom Changed', event)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
|
||||
function newQuotedAssetalances () {
|
||||
const MODULE_NAME = 'Asset Balances'
|
||||
|
||||
let thisObject = {
|
||||
visible: true,
|
||||
container: undefined,
|
||||
paramsArray: undefined,
|
||||
setParamsArray: setParamsArray,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
speedometers: [],
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.isWheelable = false
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
for (let i = 0; i < thisObject.speedometers.length; i++) {
|
||||
let speedometer = thisObject.speedometers[i]
|
||||
speedometer.finalize()
|
||||
}
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.paramsArray = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
for (let i = 0; i < 2; i++) {
|
||||
let speedometer = newSpeedometer()
|
||||
|
||||
speedometer.initialize()
|
||||
thisObject.speedometers.push(speedometer)
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point, purpose) {
|
||||
if (thisObject.visible !== true) { return }
|
||||
|
||||
let container
|
||||
for (let i = 0; i < thisObject.speedometers.length; i++) {
|
||||
container = thisObject.speedometers[i].getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
if (container.isForThisPurpose(purpose)) {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function setParamsArray (paramsArray) {
|
||||
thisObject.paramsArray = paramsArray
|
||||
for (let i = 0; i < thisObject.speedometers.length; i++) {
|
||||
let speedometer = thisObject.speedometers[i]
|
||||
speedometer.params = paramsArray[i]
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.visible !== true) { return }
|
||||
|
||||
for (let i = 0; i < thisObject.speedometers.length; i++) {
|
||||
let speedometer = thisObject.speedometers[i]
|
||||
speedometer.draw()
|
||||
}
|
||||
|
||||
drawQuotedAssetalances()
|
||||
}
|
||||
|
||||
function drawQuotedAssetalances () {
|
||||
if (thisObject.paramsArray === undefined) { return }
|
||||
|
||||
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 = 'ASSET BALANCES'
|
||||
|
||||
xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO
|
||||
yOffset = -45 + 9
|
||||
|
||||
labelPoint = {
|
||||
x: 0 - xOffset + (thisObject.paramsArray[1].LEFT_OFFSET - thisObject.paramsArray[0].LEFT_OFFSET) / 2 + thisObject.paramsArray[0].LEFT_OFFSET - 20,
|
||||
y: COCKPIT_SPACE_POSITION - COCKPIT_SPACE_HEIGHT / 2 - yOffset
|
||||
}
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,169 @@
|
||||
|
||||
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 layersToTurnOn = []
|
||||
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
|
||||
AT_FULL_SCREEN_MODE = true
|
||||
} else {
|
||||
thisObject.status = 'Off'
|
||||
goOutFullscreen()
|
||||
CURRENT_TOP_MARGIN = window.canvasApp.topMargin
|
||||
AT_FULL_SCREEN_MODE = false
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,259 @@
|
||||
|
||||
function newSpeedometer () {
|
||||
const MODULE_NAME = 'Speedometer'
|
||||
|
||||
let thisObject = {
|
||||
visible: true,
|
||||
container: undefined,
|
||||
params: 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
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.params = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
|
||||
}
|
||||
|
||||
function getContainer (point, purpose) {
|
||||
if (thisObject.visible !== true) { return }
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.params === undefined) { return }
|
||||
drawAssetCircle()
|
||||
}
|
||||
|
||||
function drawAssetCircle () {
|
||||
const RED_LINE_HIGHT = 2
|
||||
const RADIUS = 50
|
||||
const OPACITY = 1
|
||||
|
||||
let centerPoint = {
|
||||
x: thisObject.params.LEFT_OFFSET,
|
||||
y: COCKPIT_SPACE_POSITION - COCKPIT_SPACE_HEIGHT / 2 - 9
|
||||
}
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, RADIUS + RED_LINE_HIGHT, 0.85 * Math.PI, 2.15 * Math.PI)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, RADIUS, 0.0 * Math.PI, 2.0 * Math.PI)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.fill()
|
||||
|
||||
/* We draw the circle bar */
|
||||
|
||||
const DEFAULT_THICKNESS = 2
|
||||
const VALUE_THICKNESS = 8
|
||||
const VALUE_BG_THICKNESS = 1
|
||||
const BAR_RADIUS = RADIUS * 0.85
|
||||
|
||||
let BAR_START_ANGLE = 0.85 * Math.PI
|
||||
let BAR_END_ANGLE = 2.15 * Math.PI
|
||||
let CURRENT_VALUE_ANGLE = thisObject.params.VALUE * (BAR_END_ANGLE - BAR_START_ANGLE) / thisObject.params.MAX_VALUE + BAR_START_ANGLE
|
||||
let MIN_VALUE_ANGLE = thisObject.params.MIN_VALUE * (BAR_END_ANGLE - BAR_START_ANGLE) / thisObject.params.MAX_VALUE + BAR_START_ANGLE
|
||||
let INIT_VALUE_ANGLE = thisObject.params.INIT_VALUE * (BAR_END_ANGLE - BAR_START_ANGLE) / thisObject.params.MAX_VALUE + BAR_START_ANGLE
|
||||
let PROFIT_VALUE_ANGLE = CURRENT_VALUE_ANGLE
|
||||
|
||||
if (CURRENT_VALUE_ANGLE > INIT_VALUE_ANGLE) { CURRENT_VALUE_ANGLE = INIT_VALUE_ANGLE }
|
||||
if (CURRENT_VALUE_ANGLE < MIN_VALUE_ANGLE) { CURRENT_VALUE_ANGLE = MIN_VALUE_ANGLE }
|
||||
if (PROFIT_VALUE_ANGLE > BAR_END_ANGLE) { PROFIT_VALUE_ANGLE = BAR_END_ANGLE }
|
||||
|
||||
if (
|
||||
thisObject.params.VALUE > 0 &&
|
||||
thisObject.params.ASSET_NAME === thisObject.params.BASE_ASSET &&
|
||||
thisObject.params.POSITION_TAKEN === false
|
||||
) {
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, BAR_START_ANGLE, MIN_VALUE_ANGLE)
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = VALUE_BG_THICKNESS
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, MIN_VALUE_ANGLE, CURRENT_VALUE_ANGLE)
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = VALUE_BG_THICKNESS
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, CURRENT_VALUE_ANGLE, INIT_VALUE_ANGLE)
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = VALUE_BG_THICKNESS
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
if (PROFIT_VALUE_ANGLE > INIT_VALUE_ANGLE) {
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, INIT_VALUE_ANGLE, PROFIT_VALUE_ANGLE)
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = VALUE_BG_THICKNESS
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.closePath()
|
||||
}
|
||||
|
||||
browserCanvasContext.setLineDash([2, 3])
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, BAR_START_ANGLE, MIN_VALUE_ANGLE)
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = VALUE_THICKNESS
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, MIN_VALUE_ANGLE, CURRENT_VALUE_ANGLE)
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = VALUE_THICKNESS
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, CURRENT_VALUE_ANGLE, INIT_VALUE_ANGLE)
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.RED + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = VALUE_THICKNESS
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
if (PROFIT_VALUE_ANGLE > INIT_VALUE_ANGLE) {
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(centerPoint.x, centerPoint.y, BAR_RADIUS, INIT_VALUE_ANGLE, PROFIT_VALUE_ANGLE)
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = VALUE_THICKNESS
|
||||
browserCanvasContext.stroke()
|
||||
browserCanvasContext.closePath()
|
||||
}
|
||||
}
|
||||
|
||||
/* Common Variables */
|
||||
|
||||
let fontSize
|
||||
let label
|
||||
let xOffset
|
||||
let yOffset
|
||||
|
||||
/* We put the thisObject.params.VALUE in the middle */
|
||||
|
||||
fontSize = 22
|
||||
if (thisObject.params.VALUE > 999) {
|
||||
fontSize = 18
|
||||
}
|
||||
if (thisObject.params.VALUE > 9999) {
|
||||
fontSize = 14
|
||||
}
|
||||
if (thisObject.params.VALUE > 99999) {
|
||||
fontSize = 10
|
||||
}
|
||||
browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
label = thisObject.params.VALUE
|
||||
|
||||
if (isNaN(label) === false) {
|
||||
label = Number(label)
|
||||
|
||||
label = Math.trunc(label * Math.pow(10, thisObject.params.DECIMALS)) / Math.pow(10, thisObject.params.DECIMALS)
|
||||
if (label === 0) { label = label.toFixed(2) } else { label = label.toLocaleString() }
|
||||
}
|
||||
|
||||
// label = label.substring(0, 6)
|
||||
|
||||
xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + 7
|
||||
|
||||
labelPoint = {
|
||||
x: centerPoint.x - xOffset,
|
||||
y: centerPoint.y
|
||||
}
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
|
||||
/* We put the top label */
|
||||
|
||||
fontSize = 12
|
||||
|
||||
browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
label = thisObject.params.ASSET_LABEL
|
||||
|
||||
xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + 3
|
||||
yOffset = 22
|
||||
|
||||
labelPoint = {
|
||||
x: centerPoint.x - xOffset,
|
||||
y: centerPoint.y - yOffset
|
||||
}
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
|
||||
/* We put the bottom label */
|
||||
|
||||
fontSize = 12
|
||||
|
||||
browserCanvasContext.font = 'bold ' + fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
label = thisObject.params.ASSET_NAME + ' '
|
||||
|
||||
xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO + 3
|
||||
yOffset = -15
|
||||
|
||||
labelPoint = {
|
||||
x: centerPoint.x - xOffset,
|
||||
y: centerPoint.y - yOffset
|
||||
}
|
||||
|
||||
if (thisObject.params.ASSET_NAME === thisObject.params.BASE_ASSET) {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', ' + OPACITY + ')'
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')'
|
||||
}
|
||||
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
|
||||
function newContainer () {
|
||||
let thisObject = {
|
||||
id: Math.trunc(Math.random() * 10000000),
|
||||
type: 'Rectangle',
|
||||
frame: undefined,
|
||||
eventHandler: undefined,
|
||||
parentContainer: undefined,
|
||||
isDraggeable: true,
|
||||
notDraggingOnX: false,
|
||||
notDraggingOnY: false,
|
||||
isClickeable: false,
|
||||
isWheelable: false,
|
||||
detectMouseOver: false,
|
||||
name: undefined,
|
||||
fitFunction: undefined,
|
||||
isVisibleFunction: undefined,
|
||||
displace: displace,
|
||||
initialize: initialize,
|
||||
finalize: finalize,
|
||||
connectToParent: connectToParent,
|
||||
isForThisPurpose: isForThisPurpose
|
||||
}
|
||||
|
||||
let connectedToParentWidth = false
|
||||
let connectedToParentHeight = false
|
||||
let connectedToParentRadius = false
|
||||
|
||||
let onDimmensionsChangedParentEvent = false
|
||||
let onMouseOverParentEvent = false
|
||||
let onMouseNotOverParentEvent = false
|
||||
let onFocusParentEvent = false
|
||||
let onNotFocusParentEvent = false
|
||||
let onDisplaceParentEvent = false
|
||||
let onDragStartedParentEvent = false
|
||||
let onDragFinishedParentEvent = false
|
||||
|
||||
let isConnectedToParent = false
|
||||
|
||||
let dimensionsChangedEventSubscriptionId
|
||||
let onMouseOverEventSubscriptionId
|
||||
let onMouseNotOverEventSubscriptionId
|
||||
let onFocusEventSubscriptionId
|
||||
let onNotFocusEventSubscriptionId
|
||||
let onDisplaceEventSubscriptionId
|
||||
let onDragStartedEventSubscriptionId
|
||||
let onDragFinishedEventSubscriptionId
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
if (isConnectedToParent === true) {
|
||||
thisObject.parentContainer.eventHandler.stopListening(dimensionsChangedEventSubscriptionId)
|
||||
thisObject.parentContainer.eventHandler.stopListening(onMouseOverEventSubscriptionId)
|
||||
thisObject.parentContainer.eventHandler.stopListening(onMouseNotOverEventSubscriptionId)
|
||||
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()
|
||||
|
||||
thisObject.frame.finalize()
|
||||
}
|
||||
}
|
||||
|
||||
function initialize (pName, pType) {
|
||||
thisObject.name = pName
|
||||
thisObject.type = pType
|
||||
|
||||
thisObject.frame = newFrame()
|
||||
thisObject.frame.initialize(pType)
|
||||
if (pName !== undefined) {
|
||||
thisObject.frame.containerName = pName
|
||||
}
|
||||
thisObject.frame.container = thisObject
|
||||
|
||||
thisObject.eventHandler = newEventHandler()
|
||||
thisObject.eventHandler.name = pName
|
||||
}
|
||||
|
||||
function connectToParent (
|
||||
parentContainer,
|
||||
onWidth,
|
||||
onHeight,
|
||||
onRadius,
|
||||
onDimmensionsChangedEvent,
|
||||
onMouseOverEvent,
|
||||
onMouseNotOverEvent,
|
||||
onFocusEvent,
|
||||
onNotFocusEvent,
|
||||
onDisplaceEvent,
|
||||
onDragStartedEvent,
|
||||
onDragFinishedEvent
|
||||
) {
|
||||
connectedToParentWidth = onWidth
|
||||
connectedToParentHeight = onHeight
|
||||
connectedToParentRadius = onRadius
|
||||
|
||||
onDimmensionsChangedParentEvent = onDimmensionsChangedEvent
|
||||
onMouseOverParentEvent = onMouseOverEvent
|
||||
onMouseNotOverParentEvent = onMouseNotOverEvent
|
||||
onFocusParentEvent = onFocusEvent
|
||||
onNotFocusParentEvent = onNotFocusEvent
|
||||
onDisplaceParentEvent = onDisplaceEvent
|
||||
onDragStartedParentEvent = onDragStartedEvent
|
||||
onDragFinishedParentEvent = onDragFinishedEvent
|
||||
|
||||
isConnectedToParent = true
|
||||
|
||||
thisObject.frame.parentFrame = parentContainer.frame
|
||||
thisObject.parentContainer = parentContainer
|
||||
|
||||
if (onDimmensionsChangedParentEvent === true) {
|
||||
dimensionsChangedEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('Dimmensions Changed', onParentDimmensionsChanged)
|
||||
}
|
||||
if (onMouseOverParentEvent === true) {
|
||||
onMouseOverEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
}
|
||||
if (onMouseNotOverParentEvent === true) {
|
||||
onMouseNotOverEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
||||
}
|
||||
if (onFocusParentEvent === true) {
|
||||
onFocusEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onFocus', onFocus)
|
||||
}
|
||||
if (onNotFocusParentEvent === true) {
|
||||
onNotFocusEventSubscriptionId = thisObject.parentContainer.eventHandler.listenToEvent('onNotFocus', onNotFocus)
|
||||
}
|
||||
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
|
||||
}
|
||||
if (connectedToParentHeight) {
|
||||
thisObject.frame.height = thisObject.parentContainer.frame.height
|
||||
}
|
||||
if (connectedToParentRadius) {
|
||||
thisObject.frame.radius = thisObject.parentContainer.frame.radius
|
||||
}
|
||||
}
|
||||
|
||||
function onParentDimmensionsChanged (event) {
|
||||
let dimmensionsChanged = false
|
||||
if (connectedToParentWidth) {
|
||||
thisObject.frame.width = thisObject.parentContainer.frame.width
|
||||
dimmensionsChanged = true
|
||||
}
|
||||
if (connectedToParentHeight) {
|
||||
thisObject.frame.height = thisObject.parentContainer.frame.height
|
||||
dimmensionsChanged = true
|
||||
}
|
||||
if (connectedToParentRadius) {
|
||||
thisObject.frame.radius = thisObject.parentContainer.frame.radius
|
||||
dimmensionsChanged = true
|
||||
}
|
||||
|
||||
if (dimmensionsChanged) {
|
||||
thisObject.eventHandler.raiseEvent('Dimmensions Changed', event)
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
thisObject.eventHandler.raiseEvent('onMouseOver', event)
|
||||
}
|
||||
|
||||
function onMouseNotOver (event) {
|
||||
thisObject.eventHandler.raiseEvent('onMouseNotOver', event)
|
||||
}
|
||||
|
||||
function onFocus (event) {
|
||||
thisObject.eventHandler.raiseEvent('onFocus', event)
|
||||
}
|
||||
|
||||
function onNotFocus (event) {
|
||||
thisObject.eventHandler.raiseEvent('onNotFocus', event)
|
||||
}
|
||||
|
||||
function onDisplace (event) {
|
||||
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) {
|
||||
case GET_CONTAINER_PURPOSE.MOUSE_OVER:
|
||||
if (thisObject.detectMouseOver === true) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
case GET_CONTAINER_PURPOSE.MOUSE_WHEEL:
|
||||
if (thisObject.isWheelable === true) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
case GET_CONTAINER_PURPOSE.MOUSE_CLICK:
|
||||
if (thisObject.isClickeable === true) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
case GET_CONTAINER_PURPOSE.DRAGGING:
|
||||
if (thisObject.isDraggeable === true) {
|
||||
return true
|
||||
}
|
||||
break
|
||||
default: { return false }
|
||||
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function displace (point) {
|
||||
/* This function will move the container according to the vector received. If it has a visible function will use it
|
||||
to see if the container can be moved there or not. It wont if the center will not be visible.
|
||||
|
||||
Returns true if move was possible, false if not. */
|
||||
|
||||
let checkPoint = {}
|
||||
|
||||
if (thisObject.isVisibleFunction === undefined) {
|
||||
drag()
|
||||
return true
|
||||
} else {
|
||||
checkPoint.x = thisObject.frame.position.x + point.x + thisObject.frame.width / 2
|
||||
checkPoint.y = thisObject.frame.position.y + point.y + thisObject.frame.height / 2
|
||||
let isVisible = thisObject.isVisibleFunction(checkPoint)
|
||||
if (isVisible === true) {
|
||||
drag()
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
function drag () {
|
||||
if (thisObject.notDraggingOnX === false) {
|
||||
thisObject.frame.position.x = thisObject.frame.position.x + point.x
|
||||
}
|
||||
if (thisObject.notDraggingOnY === false) {
|
||||
thisObject.frame.position.y = thisObject.frame.position.y + point.y
|
||||
}
|
||||
thisObject.eventHandler.raiseEvent('onDisplace', event)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
function newSidePanel () {
|
||||
const MODULE_NAME = 'Side Panel'
|
||||
let thisObject = {
|
||||
areas: [],
|
||||
sidePanelTab: 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
|
||||
thisObject.container.status = 'hidden'
|
||||
|
||||
resize()
|
||||
return thisObject
|
||||
|
||||
function initialize () {
|
||||
thisObject.sidePanelTab = newSidePanelTab()
|
||||
thisObject.sidePanelTab.container.connectToParent(thisObject.container, false, false)
|
||||
thisObject.sidePanelTab.initialize()
|
||||
|
||||
canvas.eventHandler.listenToEvent('Browser Resized', resize)
|
||||
isInitialized = true
|
||||
}
|
||||
|
||||
function resize () {
|
||||
thisObject.container.frame.width = SIDE_PANEL_WIDTH
|
||||
thisObject.container.frame.height = browserCanvas.height // - TOP_SPACE_HEIGHT
|
||||
thisObject.container.frame.position.x = -SIDE_PANEL_WIDTH
|
||||
thisObject.container.frame.position.y = 0 // TOP_SPACE_HEIGHT
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
|
||||
container = thisObject.sidePanelTab.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (isInitialized === false) { return }
|
||||
borders()
|
||||
thisObject.sidePanelTab.draw()
|
||||
|
||||
for (let i = 0; i < thisObject.areas.length; i++) {
|
||||
let area = thisObject.areas[i]
|
||||
area.draw()
|
||||
}
|
||||
}
|
||||
|
||||
function borders () {
|
||||
let point1
|
||||
let point2
|
||||
let point3
|
||||
let point4
|
||||
|
||||
point1 = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: thisObject.container.frame.width,
|
||||
y: 0
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: thisObject.container.frame.width,
|
||||
y: thisObject.container.frame.height
|
||||
}
|
||||
|
||||
point4 = {
|
||||
x: 0,
|
||||
y: thisObject.container.frame.height
|
||||
}
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1)
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2)
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3)
|
||||
point4 = thisObject.container.frame.frameThisPoint(point4)
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
browserCanvasContext.lineTo(point3.x, point3.y)
|
||||
browserCanvasContext.lineTo(point4.x, point4.y)
|
||||
browserCanvasContext.lineTo(point1.x, point1.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
let opacity = 1
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.LIGHT_GREY + ', ' + opacity + ''
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + opacity + ''
|
||||
browserCanvasContext.lineWidth = 0.3
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
/* Shadow */
|
||||
|
||||
if (thisObject.container.status !== 'hidden') {
|
||||
for (let i = 0; i <= 30; i++) {
|
||||
opacity = 1 - (i / 300) - 0.95
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(point2.x + i, point2.y)
|
||||
browserCanvasContext.lineTo(point3.x + i, point3.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + opacity + ''
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,216 @@
|
||||
function newSidePanelTab () {
|
||||
const MODULE_NAME = 'Side Panel Tab'
|
||||
let thisObject = {
|
||||
pointerDirection: 'left',
|
||||
container: undefined,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
const TAB_WIDTH = 25
|
||||
const TAB_HEIGHT = 60
|
||||
|
||||
let isInitialized = false
|
||||
let animation = 'none'
|
||||
let ORIGINAL_PARENT_POSITON
|
||||
let xOffset = 0
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.name = MODULE_NAME
|
||||
thisObject.container.initialize()
|
||||
thisObject.container.isClickeable = true
|
||||
thisObject.container.isDraggeable = false
|
||||
|
||||
return thisObject
|
||||
|
||||
function initialize () {
|
||||
thisObject.container.frame.width = TAB_WIDTH
|
||||
thisObject.container.frame.height = TAB_HEIGHT
|
||||
thisObject.container.frame.position.x = 0 + thisObject.container.parentContainer.frame.width
|
||||
thisObject.container.frame.position.y = 20
|
||||
|
||||
ORIGINAL_PARENT_POSITON = thisObject.container.parentContainer.frame.position.x
|
||||
|
||||
thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
|
||||
isInitialized = true
|
||||
}
|
||||
|
||||
function onMouseClick (event) {
|
||||
if (thisObject.pointerDirection === 'left') {
|
||||
animation = 'opening'
|
||||
} else {
|
||||
animation = 'closing'
|
||||
}
|
||||
}
|
||||
|
||||
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 }
|
||||
animate()
|
||||
borders()
|
||||
arrow()
|
||||
}
|
||||
|
||||
function animate () {
|
||||
const STEP = 50
|
||||
|
||||
if (animation === 'opening') {
|
||||
xOffset = xOffset + STEP
|
||||
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset
|
||||
if (xOffset > thisObject.container.parentContainer.frame.width) {
|
||||
animation = 'none'
|
||||
xOffset = thisObject.container.parentContainer.frame.width
|
||||
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset
|
||||
thisObject.container.parentContainer.status = 'visible'
|
||||
thisObject.pointerDirection = 'right'
|
||||
}
|
||||
}
|
||||
|
||||
if (animation === 'closing') {
|
||||
xOffset = xOffset - STEP
|
||||
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON + xOffset
|
||||
if (xOffset < 0) {
|
||||
animation = 'none'
|
||||
xOffset = 0
|
||||
thisObject.container.parentContainer.frame.position.x = ORIGINAL_PARENT_POSITON
|
||||
thisObject.container.parentContainer.status = 'hidden'
|
||||
thisObject.pointerDirection = 'left'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function arrow () {
|
||||
let point1
|
||||
let point2
|
||||
let point3
|
||||
|
||||
if (thisObject.pointerDirection === 'left') {
|
||||
point1 = {
|
||||
x: TAB_WIDTH * 3.5 / 10,
|
||||
y: TAB_HEIGHT * 12.5 / 30
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: TAB_WIDTH * 6.5 / 10,
|
||||
y: TAB_HEIGHT * 15 / 30
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: TAB_WIDTH * 3.5 / 10,
|
||||
y: TAB_HEIGHT * 17.5 / 30
|
||||
}
|
||||
} else {
|
||||
point1 = {
|
||||
x: TAB_WIDTH * 6.5 / 10,
|
||||
y: TAB_HEIGHT * 12.5 / 30
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: TAB_WIDTH * 3.5 / 10,
|
||||
y: TAB_HEIGHT * 15 / 30
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: TAB_WIDTH * 6.5 / 10,
|
||||
y: TAB_HEIGHT * 17.5 / 30
|
||||
}
|
||||
}
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1)
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2)
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3)
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
browserCanvasContext.lineTo(point3.x, point3.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
let opacity = 1
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREY + ', ' + opacity + ''
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
|
||||
function borders () {
|
||||
let point1
|
||||
let point2
|
||||
let point3
|
||||
let point4
|
||||
|
||||
point1 = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: TAB_WIDTH,
|
||||
y: 0
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: TAB_WIDTH,
|
||||
y: TAB_HEIGHT
|
||||
}
|
||||
|
||||
point4 = {
|
||||
x: 0,
|
||||
y: TAB_HEIGHT
|
||||
}
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1)
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2)
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3)
|
||||
point4 = thisObject.container.frame.frameThisPoint(point4)
|
||||
|
||||
let opacity
|
||||
|
||||
/* Shadow */
|
||||
|
||||
for (let i = 0; i <= 5; i++) {
|
||||
opacity = 1 - (i / 100) - 0.93
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(point1.x + i, point1.y + i)
|
||||
browserCanvasContext.lineTo(point2.x + i, point2.y + i)
|
||||
browserCanvasContext.lineTo(point3.x + i, point3.y + i)
|
||||
browserCanvasContext.lineTo(point4.x + i, point4.y + i)
|
||||
browserCanvasContext.lineTo(point1.x + i, point1.y + i)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + opacity + ''
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
|
||||
/* Background and Border */
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
browserCanvasContext.lineTo(point3.x, point3.y)
|
||||
browserCanvasContext.lineTo(point4.x, point4.y)
|
||||
browserCanvasContext.lineTo(point1.x, point1.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
opacity = 1
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ''
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + opacity + ''
|
||||
browserCanvasContext.lineWidth = 0.3
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,220 @@
|
||||
function newChainAttachDetach () {
|
||||
thisObject = {
|
||||
chainDetachNode: chainDetachNode,
|
||||
chainAttachNode: chainAttachNode
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function chainDetachNode (node, rootNodes) {
|
||||
switch (node.type) {
|
||||
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)
|
||||
completeDetachment(node, rootNodes)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
default: {
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
/* Detach from parent */
|
||||
if (node.payload.parentNode !== undefined) {
|
||||
let parentNodeDefinition = APP_SCHEMA_MAP.get(node.payload.parentNode.type)
|
||||
if (parentNodeDefinition !== undefined) {
|
||||
if (parentNodeDefinition.properties !== undefined) {
|
||||
for (let i = 0; i < parentNodeDefinition.properties.length; i++) {
|
||||
let property = parentNodeDefinition.properties[i]
|
||||
if (nodeDefinition.propertyNameAtParent === property.name) {
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
node.payload.parentNode[property.name] = undefined
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
let nodePropertyArray = node.payload.parentNode[property.name]
|
||||
if (nodePropertyArray !== undefined) {
|
||||
for (let j = 0; j < nodePropertyArray.length; j++) {
|
||||
let arrayItem = nodePropertyArray[j]
|
||||
if (arrayItem.id === node.id) {
|
||||
nodePropertyArray.splice(j, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
completeDetachment(node, rootNodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function chainAttachNode (node, attachToNode, rootNodes) {
|
||||
switch (node.type) {
|
||||
|
||||
case 'Exchange Account Key': {
|
||||
node.payload.parentNode = attachToNode
|
||||
node.payload.chainParent = attachToNode
|
||||
if (node.payload.parentNode.keys !== undefined) {
|
||||
node.payload.parentNode.keys.push(node)
|
||||
} else {
|
||||
node.payload.parentNode.key = node
|
||||
}
|
||||
completeAttachment(node, rootNodes)
|
||||
}
|
||||
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)
|
||||
completeAttachment(node, rootNodes)
|
||||
}
|
||||
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)
|
||||
completeAttachment(node, rootNodes)
|
||||
}
|
||||
break
|
||||
case 'Initial Stop': {
|
||||
if (attachToNode.phases.length >= 1) {
|
||||
return
|
||||
}
|
||||
|
||||
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)
|
||||
completeAttachment(node, rootNodes)
|
||||
}
|
||||
break
|
||||
case 'Initial Take Profit': {
|
||||
if (attachToNode.phases.length >= 1) {
|
||||
return
|
||||
}
|
||||
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)
|
||||
completeAttachment(node, rootNodes)
|
||||
}
|
||||
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)
|
||||
completeAttachment(node, rootNodes)
|
||||
} 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)
|
||||
completeAttachment(node, rootNodes)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
default: {
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
node.payload.parentNode = attachToNode
|
||||
node.payload.chainParent = attachToNode
|
||||
/* Attach to new parent */
|
||||
if (node.payload.parentNode !== undefined) {
|
||||
let parentNodeDefinition = APP_SCHEMA_MAP.get(node.payload.parentNode.type)
|
||||
if (parentNodeDefinition !== undefined) {
|
||||
if (parentNodeDefinition.properties !== undefined) {
|
||||
for (let i = 0; i < parentNodeDefinition.properties.length; i++) {
|
||||
let property = parentNodeDefinition.properties[i]
|
||||
if (nodeDefinition.propertyNameAtParent === property.name) {
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
node.payload.parentNode[property.name] = node
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
if (node.payload.parentNode[property.name] === undefined) {
|
||||
node.payload.parentNode[property.name] = []
|
||||
}
|
||||
let nodePropertyArray = node.payload.parentNode[property.name]
|
||||
nodePropertyArray.push(node)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
completeAttachment(node, rootNodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function completeDetachment (node, rootNodes) {
|
||||
node.payload.parentNode = undefined
|
||||
node.payload.chainParent = undefined
|
||||
rootNodes.push(node)
|
||||
}
|
||||
|
||||
function completeAttachment (node, rootNodes) {
|
||||
for (let i = 0; i < rootNodes.length; i++) {
|
||||
let rootNode = rootNodes[i]
|
||||
if (rootNode.id === node.id) {
|
||||
rootNodes.splice(i, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
function newNodeChildren () {
|
||||
thisObject = {
|
||||
childrenCount: childrenCount
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function childrenCount (parentNode, childNode) {
|
||||
return countChildrenUiObject(parentNode, childNode)
|
||||
}
|
||||
|
||||
function countChildrenUiObject (parentNode, childNode) {
|
||||
let response = {
|
||||
childrenCount: 0,
|
||||
childIndex: undefined
|
||||
}
|
||||
let parentNodeDefinition = APP_SCHEMA_MAP.get(parentNode.type)
|
||||
if (parentNodeDefinition !== undefined) {
|
||||
if (parentNodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
|
||||
for (let i = 0; i < parentNodeDefinition.properties.length; i++) {
|
||||
let property = parentNodeDefinition.properties[i]
|
||||
if (parentNode[property.name] !== undefined) {
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
response.childrenCount++
|
||||
if (parentNode[property.name].id === childNode.id) {
|
||||
response.childIndex = response.childrenCount
|
||||
}
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(childNode.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
let nodePropertyArray = parentNode[property.name]
|
||||
for (let j = 0; j < nodePropertyArray.length; j++) {
|
||||
let child = nodePropertyArray[j]
|
||||
if (nodeDefinition.chainedToSameType === true) {
|
||||
if (j < 1) {
|
||||
response.childrenCount++
|
||||
if (child.id === childNode.id) {
|
||||
response.childIndex = response.childrenCount
|
||||
}
|
||||
}
|
||||
} else {
|
||||
response.childrenCount++
|
||||
if (child.id === childNode.id) {
|
||||
response.childIndex = response.childrenCount
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
function newNodeCloning () {
|
||||
const MODULE_NAME = 'Node Cloning'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
thisObject = {
|
||||
getNodeClone: getNodeClone
|
||||
}
|
||||
return thisObject
|
||||
|
||||
function getNodeClone (node) {
|
||||
let idConversionMap = new Map()
|
||||
|
||||
let inmatureClone = cloneNode(node)
|
||||
let matureClone = redirectReferenceParents(inmatureClone)
|
||||
|
||||
return matureClone
|
||||
|
||||
function cloneNode (node) {
|
||||
if (node === undefined) { return }
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
let object = {
|
||||
type: node.type,
|
||||
name: node.name,
|
||||
code: node.code
|
||||
}
|
||||
|
||||
/* Children Nodes */
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (node[property.name] !== undefined) {
|
||||
object[property.name] = cloneNode(node[property.name])
|
||||
}
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'array': {
|
||||
if (node[property.name] !== undefined) {
|
||||
let nodePropertyArray = node[property.name]
|
||||
object[property.name] = []
|
||||
for (let m = 0; m < nodePropertyArray.length; m++) {
|
||||
let protocolNode = cloneNode(nodePropertyArray[m])
|
||||
if (protocolNode !== undefined) {
|
||||
object[property.name].push(protocolNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object.id = newUniqueId()
|
||||
idConversionMap.set(node.id, object.id)
|
||||
|
||||
object.savedPayload = getSavedPayload(node)
|
||||
|
||||
return object
|
||||
}
|
||||
}
|
||||
|
||||
function redirectReferenceParents (node) {
|
||||
if (node === undefined) { return }
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
let object = {
|
||||
type: node.type,
|
||||
name: node.name,
|
||||
code: node.code
|
||||
}
|
||||
|
||||
/* Children Nodes */
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (node[property.name] !== undefined) {
|
||||
object[property.name] = redirectReferenceParents(node[property.name])
|
||||
}
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'array': {
|
||||
if (node[property.name] !== undefined) {
|
||||
let nodePropertyArray = node[property.name]
|
||||
object[property.name] = []
|
||||
for (let m = 0; m < nodePropertyArray.length; m++) {
|
||||
let protocolNode = redirectReferenceParents(nodePropertyArray[m])
|
||||
if (protocolNode !== undefined) {
|
||||
object[property.name].push(protocolNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object.id = node.id
|
||||
|
||||
object.savedPayload = node.savedPayload
|
||||
|
||||
if (object.savedPayload !== undefined) {
|
||||
if (object.savedPayload.referenceParent !== undefined) {
|
||||
let newId = idConversionMap.get(object.savedPayload.referenceParent.id)
|
||||
if (newId !== undefined) {
|
||||
object.savedPayload.referenceParent.id = newId
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getSavedPayload (node) {
|
||||
if (node.payload === undefined) { return }
|
||||
let savedPayload = {
|
||||
position: {
|
||||
x: node.payload.position.x,
|
||||
y: node.payload.position.y
|
||||
},
|
||||
targetPosition: {
|
||||
x: node.payload.targetPosition.x,
|
||||
y: node.payload.targetPosition.y
|
||||
},
|
||||
floatingObject: {
|
||||
isPinned: node.payload.floatingObject.isPinned,
|
||||
isFrozen: (node.payload.floatingObject.isFrozen && node.payload.floatingObject.frozenManually),
|
||||
isCollapsed: (node.payload.floatingObject.isCollapsed && node.payload.floatingObject.collapsedManually),
|
||||
angleToParent: (node.payload.floatingObject.angleToParent),
|
||||
distanceToParent: (node.payload.floatingObject.distanceToParent)
|
||||
},
|
||||
uiObject: {
|
||||
isRunning: node.payload.uiObject.isRunning,
|
||||
shortcutKey: node.payload.uiObject.shortcutKey
|
||||
}
|
||||
}
|
||||
|
||||
/* Next for the ones that have a reference parent, we include it */
|
||||
if (node.payload.referenceParent !== undefined) {
|
||||
savedPayload.referenceParent = {
|
||||
type: node.payload.referenceParent.type,
|
||||
name: node.payload.referenceParent.name,
|
||||
id: node.payload.referenceParent.id
|
||||
}
|
||||
} else {
|
||||
/* The referenceParent property can be inherited from the previous saved payload. */
|
||||
if (node.savedPayload !== undefined) {
|
||||
savedPayload.referenceParent = node.savedPayload.referenceParent
|
||||
}
|
||||
}
|
||||
|
||||
return savedPayload
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
function newNodeDeleter () {
|
||||
thisObject = {
|
||||
deleteWorkspace: deleteWorkspace,
|
||||
deleteUIObject: deleteUIObject
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function destroyUiObject (node) {
|
||||
canvas.floatingSpace.uiObjectConstructor.destroyUiObject(node.payload)
|
||||
}
|
||||
|
||||
function deleteWorkspace (node, rootNodes) {
|
||||
if (node.rootNodes !== undefined) {
|
||||
while (node.rootNodes.length > 0) {
|
||||
let rootNode = node.rootNodes[0]
|
||||
deleteUIObject(rootNode, rootNodes)
|
||||
}
|
||||
}
|
||||
|
||||
completeDeletion(node, rootNodes)
|
||||
destroyUiObject(node)
|
||||
cleanNode(node)
|
||||
}
|
||||
|
||||
function deleteUIObject (node, rootNodes) {
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
/* Remove all of its own children nodes. */
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (node[property.name] !== undefined) {
|
||||
deleteUIObject(node[property.name], rootNodes)
|
||||
}
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
let nodePropertyArray = node[property.name]
|
||||
if (nodePropertyArray !== undefined) {
|
||||
while (nodePropertyArray.length > 0) {
|
||||
deleteUIObject(nodePropertyArray[0], rootNodes)
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remove node from parent */
|
||||
if (node.payload.parentNode !== undefined) {
|
||||
let parentNodeDefinition = APP_SCHEMA_MAP.get(node.payload.parentNode.type)
|
||||
if (parentNodeDefinition !== undefined) {
|
||||
if (parentNodeDefinition.properties !== undefined) {
|
||||
for (let i = 0; i < parentNodeDefinition.properties.length; i++) {
|
||||
let property = parentNodeDefinition.properties[i]
|
||||
if (nodeDefinition.propertyNameAtParent === property.name) {
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
node.payload.parentNode[property.name] = undefined
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
let nodePropertyArray = node.payload.parentNode[property.name]
|
||||
if (nodePropertyArray !== undefined) {
|
||||
for (let j = 0; j < nodePropertyArray.length; j++) {
|
||||
let arrayItem = nodePropertyArray[j]
|
||||
if (arrayItem.id === node.id) {
|
||||
/* If this object is chained to the ones of the same type we need to give the next in the chain the reference to the current chain parent */
|
||||
if (nodeDefinition.chainedToSameType === true) {
|
||||
if (j < nodePropertyArray.length - 1) {
|
||||
let nextArrayItem = nodePropertyArray[j + 1]
|
||||
nextArrayItem.payload.chainParent = node.payload.chainParent
|
||||
}
|
||||
}
|
||||
|
||||
nodePropertyArray.splice(j, 1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
completeDeletion(node, rootNodes)
|
||||
destroyUiObject(node)
|
||||
cleanNode(node)
|
||||
}
|
||||
}
|
||||
|
||||
function completeDeletion (node, rootNodes) {
|
||||
for (let i = 0; i < rootNodes.length; i++) {
|
||||
let rootNode = rootNodes[i]
|
||||
if (rootNode.id === node.id) {
|
||||
rootNodes.splice(i, 1)
|
||||
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
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
function newOnFocus () {
|
||||
thisObject = {
|
||||
getNodeThatIsOnFocus: getNodeThatIsOnFocus
|
||||
}
|
||||
return thisObject
|
||||
|
||||
function getNodeThatIsOnFocus (node) {
|
||||
if (node === undefined) { return }
|
||||
if (node.payload === undefined) { return }
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
/* First we ask the question to ourself */
|
||||
if (node.payload.uiObject.isOnFocus === true) {
|
||||
return node
|
||||
}
|
||||
|
||||
/* Then we check all of its own children nodes. */
|
||||
if (node.payload.floatingObject.isCollapsed !== true) {
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (node[property.name] !== undefined) {
|
||||
let child
|
||||
child = getNodeThatIsOnFocus(node[property.name])
|
||||
if (child !== undefined) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
let nodePropertyArray = node[property.name]
|
||||
if (nodePropertyArray !== undefined) {
|
||||
for (let m = 0; m < nodePropertyArray.length; m++) {
|
||||
child = getNodeThatIsOnFocus(nodePropertyArray[m])
|
||||
if (child !== undefined) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
function newProtocolNode () {
|
||||
const MODULE_NAME = 'Protocol Node'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
thisObject = {
|
||||
getProtocolNode: getProtocolNode
|
||||
}
|
||||
return thisObject
|
||||
|
||||
function getProtocolNode (node, removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, lightingPath) {
|
||||
if (node === undefined) { return }
|
||||
if (node.payload === undefined) { return }
|
||||
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
if (removePersonalData === true && nodeDefinition.isPersonalData === true) { return }
|
||||
let object = {
|
||||
type: node.type,
|
||||
name: node.name,
|
||||
code: node.code
|
||||
}
|
||||
|
||||
/* Children Nodes */
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (node[property.name] !== undefined) {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (lightingPath === undefined) {
|
||||
object[property.name] = getProtocolNode(node[property.name], removePersonalData, parseCode, includeIds, includeReferencesInSavedPayload, includePayload)
|
||||
} else {
|
||||
let newLightingPath = getNewLightingPath(lightingPath, node.type, property.childType)
|
||||
if (newLightingPath !== undefined) {
|
||||
object[property.name] = getProtocolNode(node[property.name], removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, newLightingPath)
|
||||
}
|
||||
}
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'array': {
|
||||
if (node[property.name] !== undefined) {
|
||||
if (lightingPath === undefined) {
|
||||
let nodePropertyArray = node[property.name]
|
||||
object[property.name] = []
|
||||
for (let m = 0; m < nodePropertyArray.length; m++) {
|
||||
let protocolNode = getProtocolNode(nodePropertyArray[m], removePersonalData, parseCode, includeIds, includeReferencesInSavedPayload, includePayload)
|
||||
if (protocolNode !== undefined) {
|
||||
object[property.name].push(protocolNode)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let newLightingPath = getNewLightingPath(lightingPath, node.type, property.childType)
|
||||
if (newLightingPath !== undefined) {
|
||||
let nodePropertyArray = node[property.name]
|
||||
object[property.name] = []
|
||||
for (let m = 0; m < nodePropertyArray.length; m++) {
|
||||
let protocolNode = getProtocolNode(nodePropertyArray[m], removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, newLightingPath)
|
||||
if (protocolNode !== undefined) {
|
||||
object[property.name].push(protocolNode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Parent Nodes */
|
||||
if (node.payload.parentNode !== undefined) {
|
||||
let newLightingPath = getNewLightingPath(lightingPath, node.type, node.payload.parentNode.type)
|
||||
if (newLightingPath !== undefined) {
|
||||
object.parentNode = getProtocolNode(node.payload.parentNode, removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, newLightingPath)
|
||||
}
|
||||
}
|
||||
if (node.payload.referenceParent !== undefined) {
|
||||
let newLightingPath = getNewLightingPath(lightingPath, node.type, node.payload.referenceParent.type)
|
||||
if (newLightingPath !== undefined) {
|
||||
object.referenceParent = getProtocolNode(node.payload.referenceParent, removePersonalData, parseCode, includeIds, includePayload, includeReferencesInSavedPayload, newLightingPath)
|
||||
}
|
||||
}
|
||||
|
||||
if (parseCode && object.code !== undefined && nodeDefinition.editors !== undefined) {
|
||||
if (nodeDefinition.editors.config === true) {
|
||||
try {
|
||||
object.code = JSON.parse(node.code)
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> err = ' + err.stack) }
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> node.type = ' + node.type) }
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> node.name = ' + node.name) }
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> node.code = ' + node.code) }
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] getProtocolNode -> default -> node.id = ' + node.id) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (includeIds) {
|
||||
object.id = node.id
|
||||
}
|
||||
if (includePayload) {
|
||||
object.savedPayload = getSavedPayload(node, includeReferencesInSavedPayload)
|
||||
}
|
||||
|
||||
return object
|
||||
}
|
||||
}
|
||||
|
||||
function getNewLightingPath (lightingPath, currentNodeType, nextNodeType) {
|
||||
if (lightingPath === undefined) { return }
|
||||
|
||||
if (currentNodeType === undefined) { return }
|
||||
if (nextNodeType === undefined) { return }
|
||||
|
||||
let currentNodePosition = lightingPath.indexOf('->' + currentNodeType + '->')
|
||||
let nextNodePosition = lightingPath.indexOf('->' + nextNodeType + '->')
|
||||
|
||||
let newLightingPath = lightingPath.substring(nextNodePosition, lightingPath.length + 1)
|
||||
|
||||
if (nextNodePosition > currentNodePosition) {
|
||||
return newLightingPath
|
||||
} else {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function getSavedPayload (node, includeReferencesInSavedPayload) {
|
||||
if (node.payload === undefined) { return }
|
||||
let savedPayload = {
|
||||
position: {
|
||||
x: node.payload.position.x,
|
||||
y: node.payload.position.y
|
||||
},
|
||||
targetPosition: {
|
||||
x: node.payload.targetPosition.x,
|
||||
y: node.payload.targetPosition.y
|
||||
},
|
||||
floatingObject: {
|
||||
isPinned: node.payload.floatingObject.isPinned,
|
||||
isFrozen: (node.payload.floatingObject.isFrozen && node.payload.floatingObject.frozenManually),
|
||||
isCollapsed: (node.payload.floatingObject.isCollapsed && node.payload.floatingObject.collapsedManually),
|
||||
angleToParent: (node.payload.floatingObject.angleToParent),
|
||||
distanceToParent: (node.payload.floatingObject.distanceToParent)
|
||||
},
|
||||
uiObject: {
|
||||
isRunning: node.payload.uiObject.isRunning,
|
||||
shortcutKey: node.payload.uiObject.shortcutKey
|
||||
}
|
||||
}
|
||||
|
||||
if (node.payload.frame !== undefined) {
|
||||
savedPayload.frame = {
|
||||
position: {
|
||||
x: node.payload.frame.position.x,
|
||||
y: node.payload.frame.position.y
|
||||
},
|
||||
width: node.payload.frame.width,
|
||||
height: node.payload.frame.height,
|
||||
radius: node.payload.frame.radius
|
||||
}
|
||||
}
|
||||
|
||||
if (includeReferencesInSavedPayload) {
|
||||
/* Next for the ones that have a reference parent, we include it */
|
||||
if (node.payload.referenceParent !== undefined) {
|
||||
savedPayload.referenceParent = {
|
||||
type: node.payload.referenceParent.type,
|
||||
name: node.payload.referenceParent.name,
|
||||
id: node.payload.referenceParent.id
|
||||
}
|
||||
} else {
|
||||
/* The referenceParent property can be inherited from the previous saved payload. */
|
||||
if (node.savedPayload !== undefined) {
|
||||
savedPayload.referenceParent = node.savedPayload.referenceParent
|
||||
}
|
||||
}
|
||||
}
|
||||
return savedPayload
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
function newReferenceAttachDetach () {
|
||||
thisObject = {
|
||||
referenceDetachNode: referenceDetachNode,
|
||||
referenceAttachNode: referenceAttachNode
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function referenceDetachNode (node) {
|
||||
completeDetachment(node)
|
||||
}
|
||||
|
||||
function referenceAttachNode (node, attachToNode, rootNodes) {
|
||||
completeAttachment(node, attachToNode)
|
||||
}
|
||||
|
||||
function completeAttachment (node, attachToNode) {
|
||||
node.payload.referenceParent = attachToNode
|
||||
}
|
||||
|
||||
function completeDetachment (node) {
|
||||
node.payload.referenceParent = undefined
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
function newSessionFunctions () {
|
||||
thisObject = {
|
||||
runSession: runSession,
|
||||
stopSession: stopSession
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function runSession (node, functionLibraryProtocolNode, callBackFunction) {
|
||||
/* We can not run a sessionif its parent process is not running. Less if it does not have a parent. */
|
||||
if (node.payload.parentNode === undefined) {
|
||||
node.payload.uiObject.setErrorMessage('Session needs a Process Instance parent to be able to run.')
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
if (node.payload.parentNode.payload.uiObject.isRunning !== true) {
|
||||
node.payload.uiObject.setErrorMessage('Session needs a Process Instance parent to be running to be able to run.')
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
if (node.payload.referenceParent === undefined) {
|
||||
node.payload.uiObject.setErrorMessage('Parent Process Instance need to reference a Process Definition.')
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
node.payload.uiObject.run(callBackFunction)
|
||||
|
||||
let key = node.name + '-' + node.type + '-' + node.id
|
||||
|
||||
let lightingPath = '' +
|
||||
'Trading System->' +
|
||||
'Parameters->' +
|
||||
'Base Asset->Time Range->Time Frame->Slippage->Fee Structure->' +
|
||||
'Exchange Account Asset->Asset->' +
|
||||
'Strategy->' +
|
||||
'Trigger Stage->Trigger On Event->Trigger Off Event->Take Position Event->' +
|
||||
'Announcement->Telegram Bot->' +
|
||||
'Open Stage->Initial Definition->Open Execution->' +
|
||||
'Position Size->Position Rate->Formula->' +
|
||||
'Initial Stop->Initial Take Profit->' +
|
||||
'Manage Stage->' +
|
||||
'Stop->Take Profit->' +
|
||||
'Phase->Formula->Next Phase Event->' +
|
||||
'Situation->Condition->Javascript Code->' +
|
||||
'Announcement->Telegram Bot->' +
|
||||
'Close Stage->Close Execution->'
|
||||
|
||||
let tradingSystem = functionLibraryProtocolNode.getProtocolNode(node.payload.referenceParent, false, true, true, false, false, lightingPath)
|
||||
|
||||
lightingPath = '' +
|
||||
'Backtesting Session->Paper Trading Session->Fordward Testing Session->Live Trading Session->' +
|
||||
'Parameters->' +
|
||||
'Base Asset->Time Range->Time Frame->Slippage->Fee Structure->' +
|
||||
'Exchange Account Asset->Asset->'
|
||||
|
||||
let session = functionLibraryProtocolNode.getProtocolNode(node, false, true, true, false, false, lightingPath)
|
||||
|
||||
/* Raise event to run the session */
|
||||
let event = {
|
||||
session: JSON.stringify(session),
|
||||
tradingSystem: JSON.stringify(tradingSystem)
|
||||
}
|
||||
|
||||
systemEventHandler.raiseEvent(key, 'Run Session', event)
|
||||
|
||||
if (node.payload.parentNode.payload.parentNode.payload.parentNode.payload.parentNode === undefined) {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function stopSession (node, functionLibraryProtocolNode, callBackFunction) {
|
||||
let key = node.name + '-' + node.type + '-' + node.id
|
||||
systemEventHandler.raiseEvent(key, 'Stop Session')
|
||||
|
||||
node.payload.uiObject.stop(callBackFunction)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
function newShortcutKeys () {
|
||||
thisObject = {
|
||||
getNodeByShortcutKey: getNodeByShortcutKey
|
||||
}
|
||||
return thisObject
|
||||
|
||||
function getNodeByShortcutKey (node, searchingKey) {
|
||||
if (node === undefined) { return }
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
/* Check Self first */
|
||||
if (node.payload.uiObject.shortcutKey === searchingKey) {
|
||||
return node
|
||||
}
|
||||
|
||||
/* Check all of its own children nodes. */
|
||||
if (node.payload.floatingObject.isCollapsed !== true) {
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (node[property.name] !== undefined) {
|
||||
let child
|
||||
child = getNodeByShortcutKey(node[property.name], searchingKey)
|
||||
if (child !== undefined) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
let nodePropertyArray = node[property.name]
|
||||
if (nodePropertyArray !== undefined) {
|
||||
for (let m = 0; m < nodePropertyArray.length; m++) {
|
||||
child = getNodeByShortcutKey(nodePropertyArray[m], searchingKey)
|
||||
if (child !== undefined) {
|
||||
return child
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
function newTaskFunctions () {
|
||||
thisObject = {
|
||||
runTask: runTask,
|
||||
stopTask: stopTask,
|
||||
runAllTasks: runAllTasks,
|
||||
stopAllTasks: stopAllTasks
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function runTask (node, functionLibraryProtocolNode, callBackFunction) {
|
||||
/* Check if it is possible to Run or not */
|
||||
if (node.bot === undefined) {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
if (node.bot.processes.length === 0) {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
for (let i = 0; i < node.bot.processes.length; i++) {
|
||||
let process = node.bot.processes[i]
|
||||
process.payload.uiObject.run()
|
||||
}
|
||||
|
||||
let lightingPath = '->Task->' +
|
||||
'Sensor Bot Instance->' +
|
||||
'Indicator Bot Instance->' +
|
||||
'Trading Bot Instance->' +
|
||||
'Sensor Process Instance->Indicator Process Instance->Trading Process Instance->' +
|
||||
'Market Reference->' +
|
||||
'Key Instance->Exchange Account Key->' +
|
||||
'Market->Exchange Markets->Crypto Exchange->' +
|
||||
'Market Base Asset->Market Quoted Asset->Asset->' +
|
||||
'Backtesting Session->Live Trading Session->Paper Trading Session->Fordward Testing Session->' +
|
||||
'Process Definition->' +
|
||||
'Process Output->Output Dataset->Dataset Definition->Product Definition->' +
|
||||
'Process Dependencies->' +
|
||||
'Status Dependency->Status Report->Process Definition->' +
|
||||
'Data Dependency->Dataset Definition->Product Definition->' +
|
||||
'Record Definition->Record Property->Formula->' +
|
||||
'Data Building Procedure->' +
|
||||
'Procedure Initialization->Javascript Code->' +
|
||||
'Procedure Loop->Javascript Code->' +
|
||||
'Calculations Procedure->' +
|
||||
'Procedure Initialization->Javascript Code->' +
|
||||
'Procedure Loop->Javascript Code->' +
|
||||
'Status Report->' +
|
||||
'Execution Finished Event->' +
|
||||
'Execution Started Event->Execution Finished Event->Process Definition->' +
|
||||
'Sensor Bot->' +
|
||||
'Indicator Bot->' +
|
||||
'Trading Bot->' +
|
||||
'Data Mine->'
|
||||
|
||||
let definition = functionLibraryProtocolNode.getProtocolNode(node, false, true, true, false, false, lightingPath)
|
||||
|
||||
let event = {
|
||||
taskId: node.id,
|
||||
taskName: node.name,
|
||||
definition: JSON.stringify(definition) // <- We need to do this workaround in order no to send unescaped charactars to the taskManager.
|
||||
}
|
||||
|
||||
if (node.payload.parentNode === undefined) {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
systemEventHandler.raiseEvent('Task Server', 'Debug Task Started', event)
|
||||
return
|
||||
}
|
||||
|
||||
node.payload.uiObject.run(callBackFunction)
|
||||
systemEventHandler.raiseEvent('Task Manager', 'Run Task', event)
|
||||
}
|
||||
|
||||
function stopTask (node, functionLibraryProtocolNode, callBackFunction) {
|
||||
let event = {
|
||||
taskId: node.id,
|
||||
taskName: node.name
|
||||
}
|
||||
systemEventHandler.raiseEvent('Task Manager', 'Stop Task', event)
|
||||
|
||||
node.payload.uiObject.stop(callBackFunction)
|
||||
|
||||
if (node.bot === undefined) { return }
|
||||
if (node.bot.processes.length === 0) { return }
|
||||
|
||||
for (let i = 0; i < node.bot.processes.length; i++) {
|
||||
let process = node.bot.processes[i]
|
||||
process.payload.uiObject.stop()
|
||||
}
|
||||
}
|
||||
|
||||
function runAllTasks (taskManager, functionLibraryProtocolNode) {
|
||||
for (let i = 0; i < taskManager.tasks.length; i++) {
|
||||
let node = taskManager.tasks[i]
|
||||
let menu = node.payload.uiObject.menu
|
||||
|
||||
menu.internalClick('Run Task')
|
||||
}
|
||||
}
|
||||
|
||||
function stopAllTasks (taskManager, functionLibraryProtocolNode) {
|
||||
for (let i = 0; i < taskManager.tasks.length; i++) {
|
||||
let node = taskManager.tasks[i]
|
||||
let menu = node.payload.uiObject.menu
|
||||
|
||||
menu.internalClick('Stop Task')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,413 @@
|
||||
function newUiObjectsFromNodes () {
|
||||
thisObject = {
|
||||
recreateWorkspace: recreateWorkspace,
|
||||
tryToConnectChildrenWithReferenceParents: tryToConnectChildrenWithReferenceParents,
|
||||
createUiObjectFromNode: createUiObjectFromNode,
|
||||
addUIObject: addUIObject,
|
||||
addMissingChildren: addMissingChildren
|
||||
}
|
||||
|
||||
let mapOfReferenceChildren = new Map()
|
||||
let mapOfNodes
|
||||
let tasksToRun
|
||||
let sessionsToRun
|
||||
|
||||
return thisObject
|
||||
|
||||
function recreateWorkspace (node, replacingCurrentWorkspace) {
|
||||
mapOfNodes = new Map()
|
||||
tasksToRun = []
|
||||
sessionsToRun = []
|
||||
|
||||
/* Create the workspace UI OBject and then continue with the root nodes. */
|
||||
createUiObject(false, 'Workspace', node.name, node, undefined, undefined, 'Workspace')
|
||||
if (node.rootNodes !== undefined) {
|
||||
for (let i = 0; i < node.rootNodes.length; i++) {
|
||||
let rootNode = node.rootNodes[i]
|
||||
createUiObjectFromNode(rootNode, undefined, undefined)
|
||||
}
|
||||
}
|
||||
|
||||
tryToConnectChildrenWithReferenceParents()
|
||||
|
||||
if (replacingCurrentWorkspace === true) {
|
||||
// We need to wait all tasks that were potentially running to stop
|
||||
setTimeout(runTasks, 70000)
|
||||
// We give a few seconds for the tasks to start
|
||||
setTimeout(runSessions, 80000)
|
||||
} else {
|
||||
runTasks()
|
||||
// We give a few seconds for the tasks to start
|
||||
setTimeout(runSessions, 10000)
|
||||
}
|
||||
}
|
||||
|
||||
function runTasks () {
|
||||
for (let i = 0; i < tasksToRun.length; i++) {
|
||||
let node = tasksToRun[i]
|
||||
node.payload.uiObject.menu.internalClick('Run Task')
|
||||
}
|
||||
tasksToRun = undefined
|
||||
}
|
||||
|
||||
function runSessions () {
|
||||
for (let i = 0; i < sessionsToRun.length; i++) {
|
||||
let node = sessionsToRun[i]
|
||||
node.payload.uiObject.menu.internalClick('Run Session')
|
||||
}
|
||||
sessionsToRun = undefined
|
||||
}
|
||||
|
||||
function tryToConnectChildrenWithReferenceParents () {
|
||||
/* We reconstruct here the reference relationships. */
|
||||
for (const [key, node] of mapOfNodes) {
|
||||
if (node.payload === undefined) { continue }
|
||||
if (node.payload.referenceParent !== undefined) {
|
||||
if (node.payload.referenceParent.cleaned === true) {
|
||||
node.payload.referenceParent = mapOfNodes.get(node.payload.referenceParent.id)
|
||||
continue // We were referencing a deleted node, so we replace it potentially with a newly created one.
|
||||
} else {
|
||||
continue // In this case the reference is already good.
|
||||
}
|
||||
}
|
||||
if (node.savedPayload !== undefined) {
|
||||
if (node.savedPayload.referenceParent !== undefined) { // these are children recreated
|
||||
node.payload.referenceParent = mapOfNodes.get(node.savedPayload.referenceParent.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createUiObjectFromNode (node, parentNode, chainParent, positionOffset) {
|
||||
/* Get node definition */
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
/* Resolve Initial Values */
|
||||
if (nodeDefinition.initialValues !== undefined) {
|
||||
if (nodeDefinition.initialValues.code !== undefined) {
|
||||
if (node.code === undefined) {
|
||||
node.code = nodeDefinition.initialValues.code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* For the cases where an node is not chained to its parent but to the one at the parent before it at its collection */
|
||||
if (nodeDefinition.chainedToSameType === true) {
|
||||
if (parentNode !== undefined) {
|
||||
let parentNodeDefinition = APP_SCHEMA_MAP.get(parentNode.type)
|
||||
if (parentNodeDefinition !== undefined) {
|
||||
if (parentNodeDefinition.properties !== undefined) {
|
||||
for (let i = 0; i < parentNodeDefinition.properties.length; i++) {
|
||||
let property = parentNodeDefinition.properties[i]
|
||||
if (property.childType === node.type) {
|
||||
if (property.type === 'array') {
|
||||
if (parentNode[property.name] !== undefined) {
|
||||
if (parentNode[property.name].length > 1) {
|
||||
let nodeChildren = parentNode[property.name]
|
||||
for (let j = 0; j < nodeChildren.length; j++) {
|
||||
if (node.id === nodeChildren[j].id) {
|
||||
if (j > 0) {
|
||||
chainParent = nodeChildren[j - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Create Self */
|
||||
createUiObject(false, node.type, node.name, node, parentNode, chainParent, node.type, positionOffset)
|
||||
|
||||
/* Create Children */
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
if (node[property.name] !== undefined) {
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
createUiObjectFromNode(node[property.name], node, node, positionOffset)
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
let nodePropertyArray = node[property.name]
|
||||
for (let m = 0; m < nodePropertyArray.length; m++) {
|
||||
let arrayItem = nodePropertyArray[m]
|
||||
createUiObjectFromNode(arrayItem, node, node, positionOffset)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function addUIObject (parentNode, type) {
|
||||
let object = {
|
||||
name: 'New ' + type,
|
||||
type: type
|
||||
}
|
||||
|
||||
let parentNodeDefinition = APP_SCHEMA_MAP.get(parentNode.type)
|
||||
if (parentNodeDefinition !== undefined) {
|
||||
/* Resolve Initial Values */
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(object.type)
|
||||
|
||||
if (nodeDefinition.isHierarchyHead === true) {
|
||||
parentNode.rootNodes.push(object)
|
||||
createUiObject(true, object.type, object.name, object, parentNode, undefined)
|
||||
return object
|
||||
}
|
||||
|
||||
/* For the cases where an node is not chained to its parent but to the one at the parent before it at its collection */
|
||||
let chainParent = parentNode
|
||||
if (nodeDefinition.chainedToSameType === true) {
|
||||
if (parentNodeDefinition.properties !== undefined) {
|
||||
for (let i = 0; i < parentNodeDefinition.properties.length; i++) {
|
||||
let property = parentNodeDefinition.properties[i]
|
||||
if (property.childType === type) {
|
||||
if (property.type === 'array') {
|
||||
if (parentNode[property.name] !== undefined) {
|
||||
if (parentNode[property.name].length > 0) {
|
||||
let nodeChildren = parentNode[property.name]
|
||||
chainParent = nodeChildren[nodeChildren.length - 1]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (nodeDefinition !== undefined) {
|
||||
if (nodeDefinition.initialValues !== undefined) {
|
||||
if (nodeDefinition.initialValues.code !== undefined) {
|
||||
object.code = nodeDefinition.initialValues.code
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Connect to Parent */
|
||||
if (parentNodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < parentNodeDefinition.properties.length; i++) {
|
||||
let property = parentNodeDefinition.properties[i]
|
||||
if (property.childType === type) {
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
parentNode[property.name] = object
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
if (parentNode[property.name] === undefined) {
|
||||
parentNode[property.name] = []
|
||||
}
|
||||
if (property.maxItems !== undefined) {
|
||||
if (parentNode[property.name].length < property.maxItems) {
|
||||
parentNode[property.name].push(object)
|
||||
} else {
|
||||
return // Object can not be created.
|
||||
}
|
||||
} else {
|
||||
parentNode[property.name].push(object)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
createUiObject(true, object.type, object.name, object, parentNode, chainParent)
|
||||
|
||||
/* Auto Add more Children */
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
|
||||
switch (property.type) {
|
||||
case 'node': {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (property.autoAdd === true) {
|
||||
addUIObject(object, property.childType)
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
case 'array': {
|
||||
if (property.autoAdd === true) {
|
||||
if (object[property.name] === undefined) {
|
||||
object[property.name] = []
|
||||
}
|
||||
addUIObject(object, property.childType)
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return object
|
||||
}
|
||||
|
||||
function addMissingChildren (node) {
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
|
||||
/* Connect to Parent */
|
||||
if (nodeDefinition.properties !== undefined) {
|
||||
let previousPropertyName // Since there are cases where there are many properties with the same name,because they can hold nodes of different types but only one at the time, we have to avoind counting each property of those as individual children.
|
||||
for (let i = 0; i < nodeDefinition.properties.length; i++) {
|
||||
let property = nodeDefinition.properties[i]
|
||||
if (property.type === 'node') {
|
||||
if (property.name !== previousPropertyName) {
|
||||
if (node[property.name] === undefined) {
|
||||
addUIObject(node, property.childType)
|
||||
previousPropertyName = property.name
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function createUiObject (userAddingNew, uiObjectType, name, node, parentNode, chainParent, title, positionOffset) {
|
||||
let payload = {}
|
||||
|
||||
/* Mechanism related to keeping references when cloning */
|
||||
if (node.id === undefined) {
|
||||
node.id = newUniqueId()
|
||||
} else {
|
||||
let testNode = mapOfNodes.get(node.id)
|
||||
if (testNode !== undefined && testNode.cleaned !== true) {
|
||||
node.id = newUniqueId()
|
||||
}
|
||||
}
|
||||
|
||||
/* Default Naming */
|
||||
if (name === '' || name === undefined) { name = 'My ' + uiObjectType }
|
||||
|
||||
/* If we are creating this object as the result of restoring a backup, share or clone, we will bring some of the saved properties to the new running instance. */
|
||||
if (userAddingNew === false && uiObjectType !== 'Workspace') {
|
||||
/* This following info can not be missing. */
|
||||
if (node.savedPayload === undefined) {
|
||||
console.log('Node ' + node.type + ' without savedPayload.', node)
|
||||
return
|
||||
}
|
||||
if (node.savedPayload.position === undefined) {
|
||||
console.log('Node ' + node.type + ' without savedPayload.position.', node)
|
||||
return
|
||||
}
|
||||
if (node.savedPayload.targetPosition === undefined) {
|
||||
console.log('Node ' + node.type + ' without savedPayload.targetPosition.', node)
|
||||
return
|
||||
}
|
||||
|
||||
/* If there is not a position offset, which happens when we are dropping a node into the designer, we create a cero vector then. */
|
||||
if (positionOffset === undefined) {
|
||||
positionOffset = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
|
||||
/* Adding the offset to the saved positions. */
|
||||
node.savedPayload.targetPosition.x = node.savedPayload.targetPosition.x + positionOffset.x
|
||||
node.savedPayload.targetPosition.y = node.savedPayload.targetPosition.y + positionOffset.y
|
||||
node.savedPayload.position.x = node.savedPayload.position.x + positionOffset.x
|
||||
node.savedPayload.position.y = node.savedPayload.position.y + positionOffset.y
|
||||
|
||||
/* Transferring the saved payload properties into the running instance being created. */
|
||||
payload.targetPosition = {
|
||||
x: node.savedPayload.targetPosition.x,
|
||||
y: node.savedPayload.targetPosition.y
|
||||
}
|
||||
node.savedPayload.targetPosition = undefined
|
||||
|
||||
/* Reference children connection to parents */
|
||||
if (node.savedPayload.referenceParent !== undefined) {
|
||||
payload.referenceParent = mapOfNodes.get(node.savedPayload.referenceParent.id)
|
||||
|
||||
if (payload.referenceParent !== undefined) {
|
||||
mapOfReferenceChildren.set(node.id, node)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* If we are adding a new object, then we set the initial values for position and targetPosition */
|
||||
if (userAddingNew === true || uiObjectType === 'Workspace') {
|
||||
/* Workspace allways to the spawn position */
|
||||
if (uiObjectType === 'Workspace') {
|
||||
payload.position = {
|
||||
x: spawnPosition.x,
|
||||
y: spawnPosition.y
|
||||
}
|
||||
}
|
||||
|
||||
/* Chain parent pointing to the position of the chain parent if defined. */
|
||||
if (chainParent === undefined) {
|
||||
payload.targetPosition = {
|
||||
x: spawnPosition.x,
|
||||
y: spawnPosition.y
|
||||
}
|
||||
} else {
|
||||
payload.targetPosition = {
|
||||
x: chainParent.payload.position.x,
|
||||
y: chainParent.payload.position.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Setting the title */
|
||||
if (title !== undefined) {
|
||||
payload.subTitle = title
|
||||
} else {
|
||||
payload.subTitle = uiObjectType
|
||||
}
|
||||
|
||||
payload.visible = true
|
||||
payload.title = name
|
||||
payload.node = node
|
||||
payload.parentNode = parentNode
|
||||
payload.chainParent = chainParent
|
||||
payload.onMenuItemClick = canvas.designSpace.workspace.onMenuItemClick
|
||||
|
||||
node.payload = payload
|
||||
node.type = uiObjectType
|
||||
|
||||
/* Now we mount the floating object where the UIOBject will be laying on top of */
|
||||
canvas.floatingSpace.uiObjectConstructor.createUiObject(userAddingNew, payload)
|
||||
|
||||
mapOfNodes.set(node.id, node)
|
||||
|
||||
/* Check if there are tasks to run */
|
||||
if (userAddingNew === false && uiObjectType === 'Task' && node.savedPayload !== undefined) {
|
||||
if (node.savedPayload.uiObject.isRunning === true) {
|
||||
tasksToRun.push(node)
|
||||
}
|
||||
}
|
||||
|
||||
/* Check if there are sessions to run */
|
||||
if (userAddingNew === false && node.savedPayload !== undefined) {
|
||||
if (uiObjectType === 'Live Trading Session' || uiObjectType === 'Fordward Testing Session' || uiObjectType === 'Backtesting Session' || uiObjectType === 'Paper Trading Session') {
|
||||
if (node.savedPayload.uiObject.isRunning === true) {
|
||||
sessionsToRun.push(node)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,371 @@
|
||||
|
||||
function newWorkspace () {
|
||||
const MODULE_NAME = 'Workspace'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
workspaceNode: undefined,
|
||||
tradingSystem: undefined,
|
||||
container: undefined,
|
||||
enabled: false,
|
||||
nodeChildren: undefined,
|
||||
getNodeThatIsOnFocus: getNodeThatIsOnFocus,
|
||||
getNodeByShortcutKey: getNodeByShortcutKey,
|
||||
stopAllRunningTasks: stopAllRunningTasks,
|
||||
onMenuItemClick: onMenuItemClick,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
spawn: spawn,
|
||||
chainDetachNode: chainDetachNode,
|
||||
chainAttachNode: chainAttachNode,
|
||||
referenceDetachNode: referenceDetachNode,
|
||||
referenceAttachNode: referenceAttachNode,
|
||||
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
|
||||
|
||||
spawnPosition = {
|
||||
x: canvas.floatingSpace.container.frame.width / 2,
|
||||
y: canvas.floatingSpace.container.frame.height / 2
|
||||
}
|
||||
|
||||
thisObject.workspaceNode = {}
|
||||
thisObject.workspaceNode.rootNodes = []
|
||||
|
||||
let functionLibraryReferenceAttachDetach = newReferenceAttachDetach()
|
||||
let functionLibraryChainAttachDetach = newChainAttachDetach()
|
||||
let functionLibraryNodeDeleter = newNodeDeleter()
|
||||
let functionLibraryUiObjectsFromNodes = newUiObjectsFromNodes()
|
||||
let functionLibraryProtocolNode = newProtocolNode()
|
||||
let functionLibraryNodeCloning = newNodeCloning()
|
||||
let functionLibraryTaskFunctions = newTaskFunctions()
|
||||
let functionLibrarySessionFunctions = newSessionFunctions()
|
||||
let functionLibraryShortcutKeys = newShortcutKeys()
|
||||
let functionLibraryOnFocus = newOnFocus()
|
||||
|
||||
thisObject.nodeChildren = newNodeChildren()
|
||||
|
||||
let workingAtTask = 0
|
||||
let circularProgressBar = newBusyProgressBar()
|
||||
circularProgressBar.fitFunction = canvas.floatingSpace.fitIntoVisibleArea
|
||||
let droppedNode
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.definition = undefined
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.workspaceNode = undefined
|
||||
circularProgressBar.finalize()
|
||||
circularProgressBar = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
try {
|
||||
let savedWorkspace = window.localStorage.getItem(CANVAS_APP_NAME + '.' + 'Workspace')
|
||||
|
||||
if (savedWorkspace === null) {
|
||||
thisObject.workspaceNode.type = 'Workspace'
|
||||
thisObject.workspaceNode.name = 'My Workspace'
|
||||
} else {
|
||||
thisObject.workspaceNode = JSON.parse(savedWorkspace)
|
||||
}
|
||||
functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode)
|
||||
thisObject.enabled = true
|
||||
|
||||
setInterval(saveWorkspace, 5000)
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function chainDetachNode (node) {
|
||||
functionLibraryChainAttachDetach.chainDetachNode(node, thisObject.workspaceNode.rootNodes)
|
||||
}
|
||||
|
||||
function chainAttachNode (node, attachToNode) {
|
||||
functionLibraryChainAttachDetach.chainAttachNode(node, attachToNode, thisObject.workspaceNode.rootNodes)
|
||||
}
|
||||
|
||||
function referenceDetachNode (node) {
|
||||
functionLibraryReferenceAttachDetach.referenceDetachNode(node)
|
||||
}
|
||||
|
||||
function referenceAttachNode (node, attachToNode) {
|
||||
functionLibraryReferenceAttachDetach.referenceAttachNode(node, attachToNode, thisObject.workspaceNode.rootNodes)
|
||||
}
|
||||
|
||||
function saveWorkspace () {
|
||||
let textToSave = stringifyWorkspace()
|
||||
window.localStorage.setItem(CANVAS_APP_NAME + '.' + 'Workspace', textToSave)
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (thisObject.enabled !== true) { return }
|
||||
|
||||
if (workingAtTask > 0) {
|
||||
circularProgressBar.physics()
|
||||
|
||||
switch (workingAtTask) {
|
||||
case 1:
|
||||
stopAllRunningTasks()
|
||||
workingAtTask++
|
||||
break
|
||||
case 2:
|
||||
functionLibraryNodeDeleter.deleteWorkspace(thisObject.workspaceNode, thisObject.workspaceNode.rootNodes)
|
||||
workingAtTask++
|
||||
break
|
||||
case 3:
|
||||
thisObject.workspaceNode = droppedNode
|
||||
droppedNode = undefined
|
||||
workingAtTask++
|
||||
break
|
||||
case 4:
|
||||
functionLibraryUiObjectsFromNodes.recreateWorkspace(thisObject.workspaceNode, true)
|
||||
workingAtTask++
|
||||
break
|
||||
case 5:
|
||||
canvas.chartingSpace.reset()
|
||||
workingAtTask++
|
||||
break
|
||||
case 6:
|
||||
workingAtTask = 0
|
||||
circularProgressBar.visible = false
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (circularProgressBar !== undefined) {
|
||||
circularProgressBar.draw()
|
||||
}
|
||||
}
|
||||
|
||||
function stringifyWorkspace (removePersonalData) {
|
||||
let stringifyReadyNodes = []
|
||||
for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) {
|
||||
let rootNode = thisObject.workspaceNode.rootNodes[i]
|
||||
let node = functionLibraryProtocolNode.getProtocolNode(rootNode, removePersonalData, false, true, true, true)
|
||||
if (node) {
|
||||
stringifyReadyNodes.push(node)
|
||||
}
|
||||
}
|
||||
let workspace = {
|
||||
type: 'Workspace',
|
||||
name: thisObject.workspaceNode.name,
|
||||
rootNodes: stringifyReadyNodes
|
||||
}
|
||||
|
||||
return JSON.stringify(workspace)
|
||||
}
|
||||
|
||||
function stopAllRunningTasks () {
|
||||
for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) {
|
||||
let rootNode = thisObject.workspaceNode.rootNodes[i]
|
||||
if (rootNode.type === 'Network') {
|
||||
if (rootNode.networkNodes !== undefined) {
|
||||
for (let j = 0; j < rootNode.networkNodes.length; j++) {
|
||||
let networkNode = rootNode.networkNodes[j]
|
||||
if (networkNode.dataMining !== undefined) {
|
||||
for (let i = 0; i < networkNode.dataMining.taskManagers.length; i++) {
|
||||
let taskManager = networkNode.dataMining.taskManagers[i]
|
||||
taskManager.payload.uiObject.menu.internalClick('Stop All Tasks')
|
||||
}
|
||||
}
|
||||
if (networkNode.testingEnvironment !== undefined) {
|
||||
for (let i = 0; i < networkNode.testingEnvironment.taskManagers.length; i++) {
|
||||
let taskManager = networkNode.testingEnvironment.taskManagers[i]
|
||||
taskManager.payload.uiObject.menu.internalClick('Stop All Tasks')
|
||||
}
|
||||
}
|
||||
if (networkNode.productionEnvironment !== undefined) {
|
||||
for (let i = 0; i < networkNode.productionEnvironment.taskManagers.length; i++) {
|
||||
let taskManager = networkNode.productionEnvironment.taskManagers[i]
|
||||
taskManager.payload.uiObject.menu.internalClick('Stop All Tasks')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeByShortcutKey (searchingKey) {
|
||||
for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) {
|
||||
let rootNode = thisObject.workspaceNode.rootNodes[i]
|
||||
let node = functionLibraryShortcutKeys.getNodeByShortcutKey(rootNode, searchingKey)
|
||||
if (node !== undefined) { return node }
|
||||
}
|
||||
}
|
||||
|
||||
function getNodeThatIsOnFocus () {
|
||||
for (let i = 0; i < thisObject.workspaceNode.rootNodes.length; i++) {
|
||||
let rootNode = thisObject.workspaceNode.rootNodes[i]
|
||||
let node = functionLibraryOnFocus.getNodeThatIsOnFocus(rootNode)
|
||||
if (node !== undefined) { return node }
|
||||
}
|
||||
}
|
||||
|
||||
function spawn (nodeText, mousePointer) {
|
||||
try {
|
||||
let point = {
|
||||
x: mousePointer.x,
|
||||
y: mousePointer.y
|
||||
}
|
||||
point = canvas.floatingSpace.container.frame.unframeThisPoint(point)
|
||||
spawnPosition.x = point.x
|
||||
spawnPosition.y = point.y
|
||||
|
||||
droppedNode = JSON.parse(nodeText)
|
||||
|
||||
if (droppedNode.type === 'Workspace') {
|
||||
circularProgressBar.initialize(mousePointer)
|
||||
circularProgressBar.visible = true
|
||||
workingAtTask = 1
|
||||
return
|
||||
}
|
||||
|
||||
/* It does not exist, so we recreeate it respecting the inner state of each object. */
|
||||
let positionOffset = {
|
||||
x: spawnPosition.x - droppedNode.savedPayload.position.x,
|
||||
y: spawnPosition.y - droppedNode.savedPayload.position.y
|
||||
}
|
||||
|
||||
thisObject.workspaceNode.rootNodes.push(droppedNode)
|
||||
functionLibraryUiObjectsFromNodes.createUiObjectFromNode(droppedNode, undefined, undefined, positionOffset)
|
||||
functionLibraryUiObjectsFromNodes.tryToConnectChildrenWithReferenceParents()
|
||||
|
||||
droppedNode = undefined
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] spawn -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
async function onMenuItemClick (payload, action, relatedUiObject, callBackFunction) {
|
||||
switch (action) {
|
||||
case 'Add UI Object':
|
||||
{
|
||||
functionLibraryUiObjectsFromNodes.addUIObject(payload.node, relatedUiObject)
|
||||
}
|
||||
break
|
||||
case 'Add Missing Children':
|
||||
{
|
||||
functionLibraryUiObjectsFromNodes.addMissingChildren(payload.node)
|
||||
}
|
||||
break
|
||||
case 'Delete UI Object':
|
||||
{
|
||||
functionLibraryNodeDeleter.deleteUIObject(payload.node, thisObject.workspaceNode.rootNodes)
|
||||
}
|
||||
break
|
||||
case 'Share Workspace':
|
||||
{
|
||||
let text = stringifyWorkspace(true)
|
||||
let fileName = 'Share - ' + payload.node.type + ' - ' + payload.node.name + '.json'
|
||||
downloadText(fileName, text)
|
||||
}
|
||||
break
|
||||
case 'Backup Workspace':
|
||||
{
|
||||
let text = stringifyWorkspace(false)
|
||||
let fileName = 'Backup - ' + payload.node.type + ' - ' + payload.node.name + '.json'
|
||||
downloadText(fileName, text)
|
||||
}
|
||||
break
|
||||
case 'Edit Code':
|
||||
|
||||
break
|
||||
case 'Share':
|
||||
{
|
||||
let text = JSON.stringify(functionLibraryProtocolNode.getProtocolNode(payload.node, true, false, true, true, true))
|
||||
|
||||
let nodeName = payload.node.name
|
||||
if (nodeName === undefined) {
|
||||
nodeName = ''
|
||||
} else {
|
||||
nodeName = '.' + nodeName
|
||||
}
|
||||
let fileName = 'Share - ' + payload.node.type + ' - ' + nodeName + '.json'
|
||||
downloadText(fileName, text)
|
||||
}
|
||||
|
||||
break
|
||||
case 'Backup':
|
||||
{
|
||||
let text = JSON.stringify(functionLibraryProtocolNode.getProtocolNode(payload.node, false, false, true, true, true))
|
||||
|
||||
let nodeName = payload.node.name
|
||||
if (nodeName === undefined) {
|
||||
nodeName = ''
|
||||
} else {
|
||||
nodeName = ' ' + nodeName
|
||||
}
|
||||
let fileName = 'Backup - ' + payload.node.type + ' - ' + nodeName + '.json'
|
||||
downloadText(fileName, text)
|
||||
}
|
||||
|
||||
break
|
||||
case 'Clone':
|
||||
{
|
||||
let text = JSON.stringify(functionLibraryNodeCloning.getNodeClone(payload.node))
|
||||
|
||||
let nodeName = payload.node.name
|
||||
if (nodeName === undefined) {
|
||||
nodeName = ''
|
||||
} else {
|
||||
nodeName = ' ' + nodeName
|
||||
}
|
||||
let fileName = 'Clone - ' + payload.node.type + ' - ' + nodeName + '.json'
|
||||
downloadText(fileName, text)
|
||||
}
|
||||
|
||||
break
|
||||
case 'Run Task':
|
||||
{
|
||||
functionLibraryTaskFunctions.runTask(payload.node, functionLibraryProtocolNode, callBackFunction)
|
||||
}
|
||||
break
|
||||
case 'Stop Task':
|
||||
{
|
||||
functionLibraryTaskFunctions.stopTask(payload.node, functionLibraryProtocolNode, callBackFunction)
|
||||
}
|
||||
break
|
||||
case 'Run All Tasks':
|
||||
{
|
||||
functionLibraryTaskFunctions.runAllTasks(payload.node, functionLibraryProtocolNode)
|
||||
}
|
||||
break
|
||||
case 'Stop All Tasks':
|
||||
{
|
||||
functionLibraryTaskFunctions.stopAllTasks(payload.node, functionLibraryProtocolNode)
|
||||
}
|
||||
break
|
||||
case 'Run Session':
|
||||
{
|
||||
functionLibrarySessionFunctions.runSession(payload.node, functionLibraryProtocolNode, callBackFunction)
|
||||
}
|
||||
break
|
||||
case 'Stop Session':
|
||||
{
|
||||
functionLibrarySessionFunctions.stopSession(payload.node, functionLibraryProtocolNode, callBackFunction)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
|
||||
function newEventHandler () {
|
||||
let thisObject = {
|
||||
name: undefined, // This is for debugging purposes only.
|
||||
listenToEvent: listenToEvent,
|
||||
stopListening: stopListening,
|
||||
raiseEvent: raiseEvent,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let eventHandlers = [] // Here we store all the functions we will call when an event is raiseed.
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
eventHandlers = []
|
||||
}
|
||||
|
||||
function listenToEvent (eventType, handler, extraData) {
|
||||
let eventSubscriptionId = Math.trunc(Math.random() * 1000000)
|
||||
eventHandlers.push([eventType, handler, extraData, eventSubscriptionId])
|
||||
return eventSubscriptionId
|
||||
}
|
||||
|
||||
function stopListening (pEventSubscriptionId) {
|
||||
for (let i = 0; i < eventHandlers.length; i++) {
|
||||
if (pEventSubscriptionId === eventHandlers[i][3]) {
|
||||
eventHandlers.splice(i, 1)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function raiseEvent (eventType, event) {
|
||||
for (var i = 0; i < eventHandlers.length; i++) {
|
||||
/* We will execute all the functions listening to this event type. */
|
||||
|
||||
if (eventHandlers[i][0] === eventType) {
|
||||
let handler = eventHandlers[i][1]
|
||||
let extraData = eventHandlers[i][2]
|
||||
|
||||
handler(event, extraData)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
|
||||
function newBusyProgressBar () {
|
||||
const MODULE_NAME = 'Circular Progress Bar'
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
isDeployed: undefined,
|
||||
visible: undefined,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
let position
|
||||
let addRadius
|
||||
|
||||
let eventSubscriptionHeartbeat
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.payload = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
}
|
||||
|
||||
function initialize (pPosition) {
|
||||
position = pPosition
|
||||
|
||||
addRadius = 0
|
||||
}
|
||||
|
||||
function physics () {
|
||||
addRadius = addRadius + 20
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.visible !== true) { return }
|
||||
|
||||
let visiblePosition = {
|
||||
x: position.x,
|
||||
y: position.y
|
||||
}
|
||||
|
||||
visiblePosition = thisObject.fitFunction(visiblePosition)
|
||||
|
||||
const RADIUS = 10 + addRadius
|
||||
let OPACITY = 1
|
||||
|
||||
let initialAngle = 0
|
||||
let finalAngle = Math.PI * 2
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, RADIUS, initialAngle, finalAngle, false)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', 1)'
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + OPACITY * 2 + ')'
|
||||
browserCanvasContext.lineWidth = 15
|
||||
browserCanvasContext.setLineDash([3, 4])
|
||||
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = 10
|
||||
browserCanvasContext.setLineDash([3, 4])
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
|
||||
function newCircularMenu () {
|
||||
const MODULE_NAME = 'Circular Menu'
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
isDeployed: undefined,
|
||||
internalClick: internalClick,
|
||||
physics: physics,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME, 'Circle')
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.frame.radius = 0
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
let menuItems = []
|
||||
|
||||
let selfFocusEventSubscriptionId
|
||||
let selfNotFocuskEventSubscriptionId
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(selfFocusEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(selfNotFocuskEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i]
|
||||
menuItem.finalize()
|
||||
}
|
||||
|
||||
menuItems = undefined
|
||||
}
|
||||
|
||||
function initialize (menuItemsInitialValues, payload) {
|
||||
/* Create the array of Menu Items */
|
||||
|
||||
let iconAndTextArray = []
|
||||
let iconOnlyArray
|
||||
let ringsArray = [[], [], []]
|
||||
|
||||
for (let i = 0; i < menuItemsInitialValues.length; i++) {
|
||||
let menuItem = newCircularMenuItem()
|
||||
let menuItemInitialValue = menuItemsInitialValues[i]
|
||||
|
||||
menuItem.action = menuItemInitialValue.action
|
||||
menuItem.actionFunction = menuItemInitialValue.actionFunction
|
||||
menuItem.actionStatus = menuItemInitialValue.actionStatus
|
||||
menuItem.label = menuItemInitialValue.label
|
||||
menuItem.workingLabel = menuItemInitialValue.workingLabel
|
||||
menuItem.workDoneLabel = menuItemInitialValue.workDoneLabel
|
||||
menuItem.workFailedLabel = menuItemInitialValue.workFailedLabel
|
||||
menuItem.secondaryAction = menuItemInitialValue.secondaryAction
|
||||
menuItem.secondaryLabel = menuItemInitialValue.secondaryLabel
|
||||
menuItem.secondaryWorkingLabel = menuItemInitialValue.secondaryWorkingLabel
|
||||
menuItem.secondaryWorkDoneLabel = menuItemInitialValue.secondaryWorkDoneLabel
|
||||
menuItem.secondaryWorkFailedLabel = menuItemInitialValue.secondaryWorkFailedLabel
|
||||
menuItem.secondaryIcon = menuItemInitialValue.secondaryIcon
|
||||
menuItem.visible = menuItemInitialValue.visible
|
||||
menuItem.iconPathOn = menuItemInitialValue.iconPathOn
|
||||
menuItem.iconPathOff = menuItemInitialValue.iconPathOff
|
||||
menuItem.rawRadius = menuItemInitialValue.rawRadius
|
||||
menuItem.targetRadius = menuItemInitialValue.label
|
||||
menuItem.currentRadius = menuItemInitialValue.currentRadius
|
||||
menuItem.angle = menuItemInitialValue.angle
|
||||
menuItem.currentStatus = menuItemInitialValue.currentStatus
|
||||
menuItem.relatedUiObject = menuItemInitialValue.relatedUiObject
|
||||
menuItem.dontShowAtFullscreen = menuItemInitialValue.dontShowAtFullscreen
|
||||
menuItem.askConfirmation = menuItemInitialValue.askConfirmation
|
||||
menuItem.confirmationLabel = menuItemInitialValue.confirmationLabel
|
||||
menuItem.disableIfPropertyIsDefined = menuItemInitialValue.disableIfPropertyIsDefined
|
||||
menuItem.propertyToCheckFor = menuItemInitialValue.propertyToCheckFor
|
||||
menuItem.ring = menuItemInitialValue.ring
|
||||
menuItem.icons = menuItemInitialValue.icons
|
||||
|
||||
if (menuItem.label === undefined) {
|
||||
menuItem.type = 'Icon Only'
|
||||
|
||||
if (menuItem.ring === undefined) {
|
||||
menutItem.ring = 1
|
||||
}
|
||||
iconOnlyArray = ringsArray[menuItem.ring - 1]
|
||||
iconOnlyArray.push(menuItem)
|
||||
} else {
|
||||
menuItem.type = 'Icon & Text'
|
||||
iconAndTextArray.push(menuItem)
|
||||
}
|
||||
|
||||
menuItem.initialize(payload)
|
||||
menuItem.container.connectToParent(thisObject.container, false, false, true, true, false, false, true, true)
|
||||
menuItems.push(menuItem)
|
||||
}
|
||||
|
||||
/* There are 3 possible rings of icons, we will go through each of them here. */
|
||||
let amplitudeArray = [80, 50, 40]
|
||||
let initialAngleArray = [220, 205, 200]
|
||||
for (let j = 1; j < 4; j++) {
|
||||
let iconOnlyArray = ringsArray[j - 1]
|
||||
/* Here we calculate the angles for each menu item, and then apply it if it was not previously defined. */
|
||||
let amplitude = amplitudeArray[j - 1]
|
||||
let initialAngle = initialAngleArray[j - 1]
|
||||
let step = amplitude / (iconOnlyArray.length - 1)
|
||||
|
||||
for (let i = 0; i < iconOnlyArray.length; i++) {
|
||||
let menuItem = iconOnlyArray[i]
|
||||
let angle = initialAngle - step * i
|
||||
if (menuItem.angle === undefined) {
|
||||
menuItem.angle = angle
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Text and Icon */
|
||||
let amplitude = 140
|
||||
let initialAngle = -70
|
||||
step = amplitude / 7
|
||||
let currentItem = (7 - iconAndTextArray.length) / 2 + 1
|
||||
for (let i = 0; i < iconAndTextArray.length; i++) {
|
||||
let menuItem = iconAndTextArray[i]
|
||||
let angle = initialAngle + step * (currentItem - 0.5)
|
||||
currentItem++
|
||||
if (menuItem.angle === undefined) {
|
||||
menuItem.angle = angle
|
||||
}
|
||||
}
|
||||
|
||||
iconOnlyArray = undefined
|
||||
iconAndTextArray = undefined
|
||||
|
||||
/* Listen to events */
|
||||
selfFocusEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onFocus', onFocus)
|
||||
selfNotFocuskEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onNotFocus', onNotFocus)
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
|
||||
if (thisObject.isDeployed === true) {
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i]
|
||||
if (menuItem.visible === true) {
|
||||
container = menuItem.getContainer(point)
|
||||
}
|
||||
if (container !== undefined) { return container }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function internalClick (action) {
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i]
|
||||
if (menuItem.visible === true) {
|
||||
if (menuItem.nextAction === action) {
|
||||
menuItem.internalClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i]
|
||||
menuItem.physics()
|
||||
}
|
||||
}
|
||||
|
||||
function onFocus () {
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i]
|
||||
menuItem.targetRadius = menuItem.rawRadius * 1.5
|
||||
menuItem.isDeployed = true
|
||||
}
|
||||
thisObject.isDeployed = true
|
||||
}
|
||||
|
||||
function onNotFocus () {
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i]
|
||||
menuItem.targetRadius = menuItem.rawRadius * 0 - i * 4
|
||||
menuItem.isDeployed = false
|
||||
}
|
||||
thisObject.isDeployed = false
|
||||
}
|
||||
|
||||
function drawBackground (pFloatingObject) {
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i]
|
||||
if (menuItem.visible === true) {
|
||||
menuItem.drawBackground()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground (pFloatingObject) {
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menuItem = menuItems[i]
|
||||
if (menuItem.visible === true) {
|
||||
menuItem.drawForeground()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,437 @@
|
||||
|
||||
function newCircularMenuItem () {
|
||||
const MODULE_NAME = 'Circular Menu Iem'
|
||||
|
||||
let thisObject = {
|
||||
type: undefined,
|
||||
isDeployed: undefined,
|
||||
askConfirmation: undefined,
|
||||
confirmationLabel: undefined,
|
||||
iconOn: undefined,
|
||||
iconOff: undefined,
|
||||
icons: undefined,
|
||||
currentIcon: undefined,
|
||||
disableIfPropertyIsDefined: undefined,
|
||||
propertyToCheckFor: undefined,
|
||||
action: undefined,
|
||||
actionFunction: undefined,
|
||||
actionStatus: undefined,
|
||||
label: undefined,
|
||||
workingLabel: undefined,
|
||||
workDoneLabel: undefined,
|
||||
workFailedLabel: undefined,
|
||||
secondaryAction: undefined,
|
||||
secondaryLabel: undefined,
|
||||
secondaryWorkingLabel: undefined,
|
||||
secondaryWorkDoneLabel: undefined,
|
||||
secondaryWorkFailedLabel: undefined,
|
||||
secondaryIcon: undefined,
|
||||
nextAction: undefined,
|
||||
visible: false,
|
||||
iconPathOn: undefined,
|
||||
iconPathOff: undefined,
|
||||
rawRadius: undefined,
|
||||
targetRadius: undefined,
|
||||
currentRadius: undefined,
|
||||
angle: undefined,
|
||||
container: undefined,
|
||||
payload: undefined,
|
||||
relatedUiObject: undefined,
|
||||
dontShowAtFullscreen: undefined,
|
||||
isEnabled: true,
|
||||
internalClick: internalClick,
|
||||
physics: physics,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.isClickeable = true
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.detectMouseOver = true
|
||||
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 = 30
|
||||
|
||||
let isMouseOver = false
|
||||
|
||||
let selfMouseOverEventSubscriptionId
|
||||
let selfMouseClickEventSubscriptionId
|
||||
let selfMouseNotOverEventSubscriptionId
|
||||
|
||||
let labelToPrint = ''
|
||||
let defaultBackgroudColor = UI_COLOR.RED
|
||||
let backgroundColorToUse = UI_COLOR.RED
|
||||
let temporaryStatus = 0
|
||||
let temporaryStatusCounter = 0
|
||||
|
||||
const EXTRA_MOUSE_OVER_ICON_SIZE = 6
|
||||
|
||||
const STATUS_NO_ACTION_TAKEN_YET = 0
|
||||
const STATUS_PRIMARY_ACTION_WORKING = -1
|
||||
const STATUS_SECONDARY_ACTION_WORKING = -2
|
||||
const STATUS_PRIMARY_WORK_DONE = -3
|
||||
const STATUS_PRIMARY_WORK_FAILED = -4
|
||||
const STATUS_WAITING_CONFIRMATION = -5
|
||||
const STATUS_SECONDARY_WORK_DONE = -6
|
||||
const STATUS_SECONDARY_WORK_FAILED = -7
|
||||
|
||||
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
|
||||
thisObject.iconOn = undefined
|
||||
thisObject.iconOff = undefined
|
||||
thisObject.icons = undefined
|
||||
thisObject.currentIcon = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.actionFunction = undefined
|
||||
thisObject.actionStatus = undefined
|
||||
thisObject.disableIfPropertyIsDefined = undefined
|
||||
thisObject.propertyToCheckFor = undefined
|
||||
}
|
||||
|
||||
function initialize (pPayload) {
|
||||
thisObject.payload = pPayload
|
||||
|
||||
iconPhysics()
|
||||
|
||||
selfMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
|
||||
selfMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
||||
|
||||
if (thisObject.type === 'Icon & Text') {
|
||||
thisObject.container.frame.width = 150
|
||||
} else {
|
||||
thisObject.container.frame.width = 50
|
||||
}
|
||||
|
||||
thisObject.nextAction = thisObject.action
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if (thisObject.dontShowAtFullscreen === true && AT_FULL_SCREEN_MODE === true) { return }
|
||||
|
||||
let container
|
||||
if (thisObject.isDeployed === true) {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true, false) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (thisObject.dontShowAtFullscreen === true && AT_FULL_SCREEN_MODE === true) { return }
|
||||
|
||||
const INCREASE_STEP = 2
|
||||
|
||||
if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= INCREASE_STEP) {
|
||||
if (thisObject.currentRadius < thisObject.targetRadius) {
|
||||
thisObject.currentRadius = thisObject.currentRadius + INCREASE_STEP
|
||||
} else {
|
||||
thisObject.currentRadius = thisObject.currentRadius - INCREASE_STEP
|
||||
}
|
||||
}
|
||||
|
||||
let radiusGrowthFactor
|
||||
if (thisObject.type === 'Icon Only') {
|
||||
switch (thisObject.ring) {
|
||||
case 1: {
|
||||
radiusGrowthFactor = 5
|
||||
break
|
||||
}
|
||||
case 2: {
|
||||
radiusGrowthFactor = 3.5
|
||||
break
|
||||
}
|
||||
case 3: {
|
||||
radiusGrowthFactor = 2
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
radiusGrowthFactor = 4
|
||||
}
|
||||
|
||||
thisObject.container.frame.position.x = thisObject.container.frame.radius * radiusGrowthFactor / 7 * Math.cos(toRadians(thisObject.angle)) - thisObject.currentRadius * 1.5
|
||||
thisObject.container.frame.position.y = thisObject.container.frame.radius * radiusGrowthFactor / 7 * Math.sin(toRadians(thisObject.angle)) - thisObject.container.frame.height / 2
|
||||
|
||||
/* Temporary Status impacts on the label to use and the background of that label */
|
||||
if (temporaryStatusCounter > 0) {
|
||||
temporaryStatusCounter--
|
||||
}
|
||||
|
||||
if (temporaryStatusCounter === 0) {
|
||||
temporaryStatus = STATUS_NO_ACTION_TAKEN_YET
|
||||
labelToPrint = thisObject.label
|
||||
backgroundColorToUse = defaultBackgroudColor
|
||||
thisObject.nextAction = thisObject.action
|
||||
}
|
||||
|
||||
/* Here we will check if we need to monitor a property that influences the status of the Menu Item. */
|
||||
if (thisObject.disableIfPropertyIsDefined === true) {
|
||||
if (thisObject.payload.node[thisObject.propertyToCheckFor] === undefined) {
|
||||
/* This menu item is enabled. */
|
||||
thisObject.isEnabled = true
|
||||
} else {
|
||||
/* This menu item is disabled. */
|
||||
thisObject.isEnabled = false
|
||||
}
|
||||
}
|
||||
iconPhysics()
|
||||
}
|
||||
|
||||
function iconPhysics () {
|
||||
if (
|
||||
(
|
||||
temporaryStatus === STATUS_PRIMARY_WORK_DONE ||
|
||||
temporaryStatus === STATUS_SECONDARY_ACTION_WORKING ||
|
||||
temporaryStatus === STATUS_SECONDARY_WORK_DONE ||
|
||||
temporaryStatus === STATUS_SECONDARY_WORK_FAILED
|
||||
) && thisObject.secondaryAction !== undefined
|
||||
) {
|
||||
thisObject.iconOn = canvas.designSpace.iconCollection.get(thisObject.secondaryIcon)
|
||||
thisObject.iconOff = canvas.designSpace.iconCollection.get(thisObject.secondaryIcon)
|
||||
} else {
|
||||
if (thisObject.relatedUiObject !== undefined) {
|
||||
thisObject.iconOn = canvas.designSpace.iconByUiObjectType.get(thisObject.relatedUiObject)
|
||||
thisObject.iconOff = canvas.designSpace.iconByUiObjectType.get(thisObject.relatedUiObject)
|
||||
} else {
|
||||
if (thisObject.iconPathOn !== undefined && thisObject.iconPathOff !== undefined) {
|
||||
thisObject.iconOn = canvas.designSpace.iconCollection.get(thisObject.iconPathOn)
|
||||
thisObject.iconOff = canvas.designSpace.iconCollection.get(thisObject.iconPathOff)
|
||||
} else {
|
||||
thisObject.iconOn = canvas.designSpace.iconCollection.get(thisObject.icons[thisObject.actionStatus()])
|
||||
thisObject.iconOff = canvas.designSpace.iconCollection.get(thisObject.icons[thisObject.actionStatus()])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Current Status might be linked to some other object status */
|
||||
|
||||
if (thisObject.actionStatus !== undefined) {
|
||||
thisObject.currentStatus = thisObject.actionStatus()
|
||||
}
|
||||
|
||||
/* Current Status sets the icon to be used */
|
||||
|
||||
if (thisObject.currentStatus === true) {
|
||||
thisObject.icon = thisObject.iconOn
|
||||
} else {
|
||||
thisObject.icon = thisObject.iconOff
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseOver (point) {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true, false) === true) {
|
||||
isMouseOver = true
|
||||
} else {
|
||||
isMouseOver = false
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseNotOver (point) {
|
||||
isMouseOver = false
|
||||
}
|
||||
|
||||
function internalClick () {
|
||||
onMouseClick()
|
||||
}
|
||||
|
||||
function onMouseClick () {
|
||||
if (thisObject.isEnabled === false) { return }
|
||||
|
||||
if (thisObject.label === undefined) {
|
||||
/* This is what we have in the case of menu items that are only Icons. In this situation there is no complex logic, just execute the specified action. */
|
||||
thisObject.actionFunction(thisObject.payload, thisObject.action, thisObject.relatedUiObject)
|
||||
return
|
||||
}
|
||||
|
||||
if (thisObject.askConfirmation !== true) { /* No confirmation is needed */
|
||||
if (temporaryStatus === STATUS_NO_ACTION_TAKEN_YET || temporaryStatus === STATUS_PRIMARY_WORK_DONE) {
|
||||
executeAction()
|
||||
} // Any click out of those states is ignored
|
||||
} else {
|
||||
/* Confirmation is needed */
|
||||
|
||||
/* The first click ask for confirmation. */
|
||||
if (temporaryStatus === STATUS_NO_ACTION_TAKEN_YET) {
|
||||
setStatus(thisObject.confirmationLabel, UI_COLOR.GOLDEN_ORANGE, 250, STATUS_WAITING_CONFIRMATION)
|
||||
return
|
||||
}
|
||||
/* A Click during confirmation executes the pre-defined action. */
|
||||
if (temporaryStatus === STATUS_WAITING_CONFIRMATION || temporaryStatus === STATUS_PRIMARY_WORK_DONE) {
|
||||
executeAction()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function executeAction () {
|
||||
if (temporaryStatus === STATUS_NO_ACTION_TAKEN_YET || temporaryStatus === STATUS_WAITING_CONFIRMATION) {
|
||||
/* We need to execute the main Action */
|
||||
/* If there is a working label defined, we use it here. */
|
||||
if (thisObject.workingLabel !== undefined) {
|
||||
setStatus(thisObject.workingLabel, UI_COLOR.GREY, undefined, STATUS_PRIMARY_ACTION_WORKING) // Status will not expire, will only change with a callback. Mouse Clicks will be ignored.
|
||||
}
|
||||
|
||||
/* Execute the action and wait for callbacks to update our statuus. */
|
||||
thisObject.actionFunction(thisObject.payload, thisObject.action, thisObject.relatedUiObject, onPrimaryCallBack)
|
||||
return
|
||||
}
|
||||
if (temporaryStatus === STATUS_PRIMARY_WORK_DONE && thisObject.secondaryAction !== undefined) {
|
||||
/* We need to execute the secondary action. */
|
||||
if (thisObject.secondaryWorkingLabel !== undefined) {
|
||||
setStatus(thisObject.secondaryWorkingLabel, UI_COLOR.GREY, undefined, STATUS_SECONDARY_ACTION_WORKING) // Status will not expire, will only change with a callback. Mouse Clicks will be ignored.
|
||||
}
|
||||
|
||||
/* Execute the action and wait for callbacks to update our statuus. */
|
||||
thisObject.actionFunction(thisObject.payload, thisObject.secondaryAction, thisObject.relatedUiObject, onSecondaryCallBack)
|
||||
return
|
||||
}
|
||||
|
||||
function onPrimaryCallBack (err, event) {
|
||||
/* While the primary action is being executed some event might have happen. Following we process the ones we can */
|
||||
|
||||
if (event !== undefined) {
|
||||
if (event.type === 'Secondary Action Already Executed') {
|
||||
setStatus(thisObject.secondaryWorkDoneLabel, UI_COLOR.PATINATED_TURQUOISE, 100, STATUS_SECONDARY_WORK_DONE)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
/* If there is a secondary action we will act different that if there is not */
|
||||
if (thisObject.secondaryAction === undefined) { // This means there are no more possible actions.
|
||||
if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) {
|
||||
if (thisObject.workDoneLabel !== undefined) {
|
||||
setStatus(thisObject.workDoneLabel, UI_COLOR.PATINATED_TURQUOISE, 100, STATUS_PRIMARY_WORK_DONE)
|
||||
}
|
||||
} else {
|
||||
if (thisObject.workFailedLabel != undefined) {
|
||||
setStatus(thisObject.workFailedLabel, UI_COLOR.TITANIUM_YELLOW, 100, STATUS_PRIMARY_WORK_FAILED)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) {
|
||||
if (thisObject.workDoneLabel !== undefined) {
|
||||
thisObject.nextAction = thisObject.secondaryAction
|
||||
setStatus(thisObject.secondaryLabel, defaultBackgroudColor, undefined, STATUS_PRIMARY_WORK_DONE)
|
||||
}
|
||||
} else {
|
||||
if (thisObject.workFailedLabel != undefined) {
|
||||
setStatus(thisObject.workFailedLabel, UI_COLOR.TITANIUM_YELLOW, 100, STATUS_PRIMARY_WORK_FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function onSecondaryCallBack (err) {
|
||||
if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) {
|
||||
if (thisObject.secondaryWorkDoneLabel !== undefined) {
|
||||
setStatus(thisObject.secondaryWorkDoneLabel, UI_COLOR.PATINATED_TURQUOISE, 100, STATUS_SECONDARY_WORK_DONE)
|
||||
}
|
||||
} else {
|
||||
if (thisObject.secondaryWorkFailedLabel != undefined) {
|
||||
setStatus(thisObject.secondaryWorkFailedLabel, UI_COLOR.TITANIUM_YELLOW, 100, STATUS_SECONDARY_WORK_FAILED)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setStatus (text, backgroundColor, waitingCycles, newStatus) {
|
||||
labelToPrint = text
|
||||
backgroundColorToUse = backgroundColor
|
||||
temporaryStatus = newStatus
|
||||
temporaryStatusCounter = newStatus // This will often put this into negatives numbers, which will disable the counting back and automatic reseting.
|
||||
if (waitingCycles !== undefined) { // This will override the often negative value with a positive one that will tend to zero onto the default state.
|
||||
temporaryStatusCounter = waitingCycles
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
if (thisObject.dontShowAtFullscreen === true && AT_FULL_SCREEN_MODE === true) { return }
|
||||
|
||||
if (thisObject.container.frame.position.x > 0 && thisObject.isDeployed === true && thisObject.currentRadius >= thisObject.targetRadius) {
|
||||
if (thisObject.type === 'Icon & Text') {
|
||||
let backgroundColor = backgroundColorToUse
|
||||
if (thisObject.isEnabled === false) {
|
||||
backgroundColor = UI_COLOR.GREY
|
||||
}
|
||||
let params = {
|
||||
cornerRadius: 3,
|
||||
lineWidth: 0.1,
|
||||
container: thisObject.container,
|
||||
borderColor: UI_COLOR.DARK,
|
||||
backgroundColor: backgroundColor,
|
||||
castShadow: false,
|
||||
xOffset: 40
|
||||
}
|
||||
|
||||
if (isMouseOver === true) {
|
||||
params.opacity = 1
|
||||
} else {
|
||||
params.opacity = 0.8
|
||||
}
|
||||
|
||||
roundedCornersBackground(params)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
if (thisObject.dontShowAtFullscreen === true && AT_FULL_SCREEN_MODE === true) { return }
|
||||
|
||||
let menuPosition = {
|
||||
x: thisObject.currentRadius * 1.5,
|
||||
y: thisObject.container.frame.height / 2
|
||||
}
|
||||
|
||||
menuPosition = thisObject.container.frame.frameThisPoint(menuPosition)
|
||||
|
||||
/* Menu Item */
|
||||
|
||||
let iconSize
|
||||
if (isMouseOver === true) {
|
||||
iconSize = thisObject.currentRadius + EXTRA_MOUSE_OVER_ICON_SIZE
|
||||
} else {
|
||||
iconSize = thisObject.currentRadius
|
||||
}
|
||||
|
||||
if (thisObject.icon === undefined) { return }
|
||||
if (thisObject.icon.canDrawIcon === true && thisObject.currentRadius > 1 && thisObject.isDeployed === true) {
|
||||
browserCanvasContext.drawImage(thisObject.icon, menuPosition.x - iconSize, menuPosition.y - iconSize, iconSize * 2, iconSize * 2)
|
||||
|
||||
/* Menu Label */
|
||||
|
||||
if (thisObject.type === 'Icon & Text') {
|
||||
let labelPoint
|
||||
let fontSize = 10
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
if (thisObject.currentRadius >= thisObject.targetRadius) {
|
||||
labelPoint = {
|
||||
x: menuPosition.x + thisObject.currentRadius + 20,
|
||||
y: menuPosition.y + fontSize * FONT_ASPECT_RATIO
|
||||
}
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
browserCanvasContext.fillText(labelToPrint, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
|
||||
function newCircularProgressBar () {
|
||||
const MODULE_NAME = 'Circular Progress Bar'
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
isDeployed: undefined,
|
||||
physics: physics,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME, 'Circle')
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.frame.radius = 0
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
let opacityCounters = []
|
||||
|
||||
let eventSubscriptionIdHeartbeat
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id
|
||||
systemEventHandler.stopListening(key, eventSubscriptionIdHeartbeat)
|
||||
|
||||
thisObject.container = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
}
|
||||
|
||||
function initialize (payload) {
|
||||
thisObject.payload = payload
|
||||
|
||||
for (let i = 0; i < 60; i++) {
|
||||
opacityCounters.push(0)
|
||||
}
|
||||
|
||||
let key = thisObject.payload.node.name + '-' + thisObject.payload.node.type + '-' + thisObject.payload.node.id
|
||||
systemEventHandler.listenToEvent(key, 'Heartbeat', undefined, key, onResponse, onHeartBeat)
|
||||
|
||||
function onResponse (message) {
|
||||
eventSubscriptionIdHeartbeat = message.eventSubscriptionId
|
||||
}
|
||||
}
|
||||
|
||||
function onHeartBeat (message) {
|
||||
if (thisObject.payload === undefined) { return }
|
||||
if (thisObject.payload.uiObject === undefined) { return }
|
||||
opacityCounters[message.event.seconds] = 2000
|
||||
thisObject.payload.uiObject.setValue(message.event.processingDate)
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
|
||||
for (let i = 0; i < menuItems.length; i++) {
|
||||
let menutItem = menuItems[i]
|
||||
container = menutItem.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
for (let i = 0; i < 60; i++) {
|
||||
opacityCounters[i] = opacityCounters[i] - 2
|
||||
|
||||
if (opacityCounters[i] < 0) {
|
||||
opacityCounters[i] = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground (pFloatingObject) {
|
||||
|
||||
}
|
||||
|
||||
function drawBackground (pFloatingObject) {
|
||||
const VISIBLE_RADIUS = thisObject.container.frame.radius * 2
|
||||
|
||||
let visiblePosition = {
|
||||
x: thisObject.container.frame.position.x,
|
||||
y: thisObject.container.frame.position.y
|
||||
}
|
||||
|
||||
visiblePosition = thisObject.container.frame.frameThisPoint(visiblePosition)
|
||||
visiblePosition = thisObject.fitFunction(visiblePosition)
|
||||
|
||||
for (let i = 0; i < 60; i++) {
|
||||
let OPACITY = opacityCounters[i] / 1000
|
||||
|
||||
if (OPACITY === 0) { continue }
|
||||
|
||||
let initialAngle = Math.PI * 2 / 60 * i - Math.PI / 2
|
||||
let finalAngle = Math.PI * 2 / 60 * (i + 1) - Math.PI / 2
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(visiblePosition.x, visiblePosition.y, VISIBLE_RADIUS, initialAngle, finalAngle, false)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GOLDEN_ORANGE + ', ' + OPACITY * 2 + ')'
|
||||
browserCanvasContext.lineWidth = 6 + opacityCounters[i] / 100 / 2
|
||||
browserCanvasContext.setLineDash([3, 4])
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.lineWidth = 0 + opacityCounters[i] / 100 / 2
|
||||
browserCanvasContext.setLineDash([3, 4])
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
function newCodeEditor () {
|
||||
const MODULE_NAME = 'Code Editor'
|
||||
|
||||
let thisObject = {
|
||||
isVisibleFunction: undefined,
|
||||
iconOK: undefined,
|
||||
iconNOT_OK: undefined,
|
||||
visible: false,
|
||||
imagePathOK: undefined,
|
||||
imagePathNOT_OK: undefined,
|
||||
rawRadius: undefined,
|
||||
targetRadius: undefined,
|
||||
currentRadius: undefined,
|
||||
container: undefined,
|
||||
payload: undefined,
|
||||
deactivate: deactivate,
|
||||
activate: activate,
|
||||
physics: physics,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.detectMouseOver = false
|
||||
thisObject.container.frame.radius = 0
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
let isMouseOver = false
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.iconOK = undefined
|
||||
thisObject.iconNOT_OK = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.isVisibleFunction = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
/* Load Images */
|
||||
|
||||
thisObject.iconOK = new Image()
|
||||
thisObject.iconOK.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
thisObject.iconNOT_OK = new Image()
|
||||
thisObject.iconNOT_OK.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
thisObject.canDrawIcon = true
|
||||
}
|
||||
thisObject.iconNOT_OK.src = window.canvasApp.urlPrefix + thisObject.imagePathNOT_OK
|
||||
}
|
||||
// thisObject.iconOK.src = window.canvasApp.urlPrefix + thisObject.imagePathOK
|
||||
// thisObject.icon = thisObject.iconOK // The default value is ON.
|
||||
}
|
||||
|
||||
function deactivate () {
|
||||
if (thisObject.visible === true) {
|
||||
thisObject.visible = false
|
||||
let textArea = document.getElementById('textArea')
|
||||
textArea.style.display = 'none'
|
||||
thisObject.payload.node.code = textArea.value
|
||||
}
|
||||
}
|
||||
|
||||
function activate (payload) {
|
||||
thisObject.visible = true
|
||||
thisObject.payload = payload
|
||||
thisObject.rawRadius = 8
|
||||
thisObject.targetRadius = thisObject.container.frame.radius
|
||||
thisObject.currentRadius = 0
|
||||
thisObject.payload.uiObject.setErrorMessage('', 0)
|
||||
|
||||
let textArea = document.getElementById('textArea')
|
||||
textArea.value = payload.node.code
|
||||
textArea.style = 'resize: none;' +
|
||||
' border: none;' +
|
||||
' outline: none;' +
|
||||
'box-shadow: none;' +
|
||||
'overflow:hidden;' +
|
||||
'font-family: ' + UI_FONT.PRIMARY + ';' +
|
||||
'font-size: 12px;' +
|
||||
'background-color: rgb(' + UI_COLOR.RUSTED_RED + ');' +
|
||||
'color:rgb(255, 255, 255);' +
|
||||
'width: ' + thisObject.container.frame.width + 'px;' +
|
||||
'height: ' + thisObject.container.frame.height + 'px'
|
||||
textArea.style.display = 'block'
|
||||
textArea.focus()
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
if (thisObject.visible === true) {
|
||||
if (point.x === VERY_LARGE_NUMBER) {
|
||||
/* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that
|
||||
anyone can react. In our case, the code editor has a text area that is not part of the canvas, so the event is
|
||||
triggered. We compensate recognizing this coordinate and returning our container. */
|
||||
return thisObject.container
|
||||
}
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true, false) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= 0.5) {
|
||||
if (thisObject.currentRadius < thisObject.targetRadius) {
|
||||
thisObject.currentRadius = thisObject.currentRadius + 0.5
|
||||
} else {
|
||||
thisObject.currentRadius = thisObject.currentRadius - 0.5
|
||||
}
|
||||
}
|
||||
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
thisObject.container.frame.width = thisObject.container.frame.radius * 1.8 * 2
|
||||
thisObject.container.frame.height = thisObject.container.frame.radius * 1 * 2
|
||||
|
||||
let textAreaPosition = {
|
||||
x: 0 - thisObject.container.frame.width / 2,
|
||||
y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN
|
||||
}
|
||||
|
||||
textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition)
|
||||
if (thisObject.visible === true) {
|
||||
let checkPosition = {
|
||||
x: textAreaPosition.x,
|
||||
y: textAreaPosition.y - CURRENT_TOP_MARGIN
|
||||
}
|
||||
if (thisObject.isVisibleFunction(checkPosition) === false) {
|
||||
deactivate()
|
||||
}
|
||||
}
|
||||
if (thisObject.visible === true) {
|
||||
let textAreaDiv = document.getElementById('textAreaDiv')
|
||||
textAreaDiv.style = 'position:fixed; top:' + textAreaPosition.y + 'px; left:' + textAreaPosition.x + 'px; z-index:1; '
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
if (thisObject.visible === true) {
|
||||
let position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
position = thisObject.container.frame.frameThisPoint(position)
|
||||
|
||||
let radius = thisObject.container.frame.radius * 2
|
||||
|
||||
if (radius > 0.5) {
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(position.x, position.y, radius * 1.3 + 3, 0, Math.PI * 2, true)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + 1 + ')'
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(position.x, position.y, radius * 1.3, 0, Math.PI * 2, true)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + 1 + ')'
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
function drawForeground () {
|
||||
let iconPosition = {
|
||||
x: 0,
|
||||
y: thisObject.currentRadius * 1.5
|
||||
}
|
||||
|
||||
iconPosition = thisObject.container.frame.frameThisPoint(iconPosition)
|
||||
|
||||
/* Menu Item */
|
||||
|
||||
if (thisObject.canDrawIcon === true && thisObject.currentRadius > 1) {
|
||||
browserCanvasContext.drawImage(thisObject.icon, iconPosition.x - thisObject.currentRadius, iconPosition.y - thisObject.currentRadius, thisObject.currentRadius * 2, thisObject.currentRadius * 2)
|
||||
|
||||
/* Menu Label */
|
||||
|
||||
let label = 'Code looks good!'
|
||||
let labelPoint
|
||||
let fontSize = 10
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
if (label !== undefined && thisObject.currentRadius >= thisObject.targetRadius) {
|
||||
labelPoint = {
|
||||
x: iconPosition.x + thisObject.currentRadius + 10,
|
||||
y: iconPosition.y + fontSize * FONT_ASPECT_RATIO
|
||||
}
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
function newConditionEditor () {
|
||||
const MODULE_NAME = 'Condition Editor'
|
||||
|
||||
let thisObject = {
|
||||
isVisibleFunction: undefined,
|
||||
iconOK: undefined,
|
||||
iconNOT_OK: undefined,
|
||||
visible: false,
|
||||
imagePathOK: undefined,
|
||||
imagePathNOT_OK: undefined,
|
||||
rawRadius: undefined,
|
||||
targetRadius: undefined,
|
||||
currentRadius: undefined,
|
||||
container: undefined,
|
||||
payload: undefined,
|
||||
deactivate: deactivate,
|
||||
activate: activate,
|
||||
physics: physics,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.detectMouseOver = false
|
||||
thisObject.container.frame.radius = 0
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
let isMouseOver = false
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.iconOK = undefined
|
||||
thisObject.iconNOT_OK = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.isVisibleFunction = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
/* Load Images */
|
||||
|
||||
thisObject.iconOK = new Image()
|
||||
thisObject.iconOK.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
thisObject.iconNOT_OK = new Image()
|
||||
thisObject.iconNOT_OK.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
thisObject.canDrawIcon = true
|
||||
}
|
||||
thisObject.iconNOT_OK.src = window.canvasApp.urlPrefix + thisObject.imagePathNOT_OK
|
||||
}
|
||||
// thisObject.iconOK.src = window.canvasApp.urlPrefix + thisObject.imagePathOK
|
||||
// thisObject.icon = thisObject.iconOK // The default value is ON.
|
||||
}
|
||||
|
||||
function deactivate () {
|
||||
if (thisObject.visible === true) {
|
||||
thisObject.visible = false
|
||||
let textArea = document.getElementById('textArea')
|
||||
textArea.style.display = 'none'
|
||||
thisObject.payload.node.code = textArea.value
|
||||
}
|
||||
}
|
||||
|
||||
function activate (payload) {
|
||||
thisObject.visible = true
|
||||
thisObject.payload = payload
|
||||
thisObject.rawRadius = 8
|
||||
thisObject.targetRadius = thisObject.container.frame.radius
|
||||
thisObject.currentRadius = 0
|
||||
thisObject.payload.uiObject.setErrorMessage('', 0)
|
||||
|
||||
let textArea = document.getElementById('textArea')
|
||||
textArea.value = payload.node.code
|
||||
textArea.style = 'resize: none;' +
|
||||
' border: none;' +
|
||||
' outline: none;' +
|
||||
'box-shadow: none;' +
|
||||
'overflow:hidden;' +
|
||||
'font-family: ' + UI_FONT.PRIMARY + ';' +
|
||||
'font-size: 12px;' +
|
||||
'background-color: rgb(' + UI_COLOR.GREEN + ');' +
|
||||
'color:rgb(255, 255, 255);' +
|
||||
'width: ' + thisObject.container.frame.width + 'px;' +
|
||||
'height: ' + thisObject.container.frame.height + 'px'
|
||||
textArea.style.display = 'block'
|
||||
textArea.focus()
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
if (thisObject.visible === true) {
|
||||
if (point.x === VERY_LARGE_NUMBER) {
|
||||
/* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that
|
||||
anyone can react. In our case, the code editor has a text area that is not part of the canvas, so the event is
|
||||
triggered. We compensate recognizing this coordinate and returning our container. */
|
||||
return thisObject.container
|
||||
}
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true, false) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= 0.5) {
|
||||
if (thisObject.currentRadius < thisObject.targetRadius) {
|
||||
thisObject.currentRadius = thisObject.currentRadius + 0.5
|
||||
} else {
|
||||
thisObject.currentRadius = thisObject.currentRadius - 0.5
|
||||
}
|
||||
}
|
||||
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
thisObject.container.frame.width = thisObject.container.frame.radius * 1.8 * 2
|
||||
thisObject.container.frame.height = thisObject.container.frame.radius * 1 * 2
|
||||
|
||||
let textAreaPosition = {
|
||||
x: 0 - thisObject.container.frame.width / 2,
|
||||
y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN
|
||||
}
|
||||
|
||||
textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition)
|
||||
if (thisObject.visible === true) {
|
||||
let checkPosition = {
|
||||
x: textAreaPosition.x,
|
||||
y: textAreaPosition.y - CURRENT_TOP_MARGIN
|
||||
}
|
||||
if (thisObject.isVisibleFunction(checkPosition) === false) {
|
||||
deactivate()
|
||||
}
|
||||
}
|
||||
if (thisObject.visible === true) {
|
||||
let textAreaDiv = document.getElementById('textAreaDiv')
|
||||
textAreaDiv.style = 'position:fixed; top:' + textAreaPosition.y + 'px; left:' + textAreaPosition.x + 'px; z-index:1; '
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
if (thisObject.visible === true) {
|
||||
let position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
position = thisObject.container.frame.frameThisPoint(position)
|
||||
|
||||
let radius = thisObject.container.frame.radius * 2
|
||||
|
||||
if (radius > 0.5) {
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(position.x, position.y, radius * 1.3 + 3, 0, Math.PI * 2, true)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + 1 + ')'
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(position.x, position.y, radius * 1.3, 0, Math.PI * 2, true)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', ' + 1 + ')'
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
function drawForeground () {
|
||||
let iconPosition = {
|
||||
x: 0,
|
||||
y: thisObject.currentRadius * 1.5
|
||||
}
|
||||
|
||||
iconPosition = thisObject.container.frame.frameThisPoint(iconPosition)
|
||||
|
||||
/* Menu Item */
|
||||
|
||||
if (thisObject.canDrawIcon === true && thisObject.currentRadius > 1) {
|
||||
browserCanvasContext.drawImage(thisObject.icon, iconPosition.x - thisObject.currentRadius, iconPosition.y - thisObject.currentRadius, thisObject.currentRadius * 2, thisObject.currentRadius * 2)
|
||||
|
||||
/* Menu Label */
|
||||
|
||||
let label = 'Code looks good!'
|
||||
let labelPoint
|
||||
let fontSize = 10
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
if (label !== undefined && thisObject.currentRadius >= thisObject.targetRadius) {
|
||||
labelPoint = {
|
||||
x: iconPosition.x + thisObject.currentRadius + 10,
|
||||
y: iconPosition.y + fontSize * FONT_ASPECT_RATIO
|
||||
}
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,222 @@
|
||||
|
||||
function newConfigEditor () {
|
||||
const MODULE_NAME = 'Config Editor'
|
||||
|
||||
let thisObject = {
|
||||
isVisibleFunction: undefined,
|
||||
iconOK: undefined,
|
||||
iconNOT_OK: undefined,
|
||||
visible: false,
|
||||
imagePathOK: undefined,
|
||||
imagePathNOT_OK: undefined,
|
||||
rawRadius: undefined,
|
||||
targetRadius: undefined,
|
||||
currentRadius: undefined,
|
||||
container: undefined,
|
||||
payload: undefined,
|
||||
deactivate: deactivate,
|
||||
activate: activate,
|
||||
physics: physics,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.detectMouseOver = false
|
||||
thisObject.container.frame.radius = 0
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
let isMouseOver = false
|
||||
let SIZE_FACTOR = 0.6
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.iconOK = undefined
|
||||
thisObject.iconNOT_OK = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.isVisibleFunction = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
/* Load Images */
|
||||
|
||||
thisObject.iconOK = new Image()
|
||||
thisObject.iconOK.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
thisObject.iconNOT_OK = new Image()
|
||||
thisObject.iconNOT_OK.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
thisObject.canDrawIcon = true
|
||||
}
|
||||
thisObject.iconNOT_OK.src = window.canvasApp.urlPrefix + thisObject.imagePathNOT_OK
|
||||
}
|
||||
// thisObject.iconOK.src = window.canvasApp.urlPrefix + thisObject.imagePathOK
|
||||
// thisObject.icon = thisObject.iconOK // The default value is ON.
|
||||
}
|
||||
|
||||
function deactivate () {
|
||||
if (thisObject.visible === true) {
|
||||
thisObject.visible = false
|
||||
let textArea = document.getElementById('textArea')
|
||||
textArea.style.display = 'none'
|
||||
thisObject.payload.node.code = textArea.value
|
||||
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
} catch (err) {
|
||||
thisObject.payload.uiObject.setErrorMessage(err.message, 10)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function activate (payload) {
|
||||
thisObject.visible = true
|
||||
thisObject.payload = payload
|
||||
thisObject.rawRadius = 8
|
||||
thisObject.targetRadius = thisObject.container.frame.radius
|
||||
thisObject.currentRadius = 0
|
||||
thisObject.payload.uiObject.setErrorMessage('', 0)
|
||||
|
||||
let textArea = document.getElementById('textArea')
|
||||
textArea.value = payload.node.code
|
||||
textArea.style = 'resize: none;' +
|
||||
' border: none;' +
|
||||
' outline: none;' +
|
||||
'box-shadow: none;' +
|
||||
'overflow:hidden;' +
|
||||
'font-family: ' + UI_FONT.PRIMARY + ';' +
|
||||
'font-size: 12px;' +
|
||||
'background-color: rgb(' + UI_COLOR.DARK_TURQUOISE + ');' +
|
||||
'color:rgb(255, 255, 255);' +
|
||||
'width: ' + thisObject.container.frame.width + 'px;' +
|
||||
'height: ' + thisObject.container.frame.height + 'px'
|
||||
textArea.style.display = 'block'
|
||||
textArea.focus()
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
if (thisObject.visible === true) {
|
||||
if (point.x === VERY_LARGE_NUMBER) {
|
||||
/* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that
|
||||
anyone can react. In our case, the code editor has a text area that is not part of the canvas, so the event is
|
||||
triggered. We compensate recognizing this coordinate and returning our container. */
|
||||
return thisObject.container
|
||||
}
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true, false) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= 0.5) {
|
||||
if (thisObject.currentRadius < thisObject.targetRadius) {
|
||||
thisObject.currentRadius = thisObject.currentRadius + 0.5
|
||||
} else {
|
||||
thisObject.currentRadius = thisObject.currentRadius - 0.5
|
||||
}
|
||||
}
|
||||
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
thisObject.container.frame.width = thisObject.container.frame.radius * 1.8 * SIZE_FACTOR
|
||||
thisObject.container.frame.height = thisObject.container.frame.radius * 1 * SIZE_FACTOR
|
||||
|
||||
let textAreaPosition = {
|
||||
x: 0 - thisObject.container.frame.width / 2,
|
||||
y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN
|
||||
}
|
||||
|
||||
textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition)
|
||||
if (thisObject.visible === true) {
|
||||
let checkPosition = {
|
||||
x: textAreaPosition.x,
|
||||
y: textAreaPosition.y - CURRENT_TOP_MARGIN
|
||||
}
|
||||
if (thisObject.isVisibleFunction(checkPosition) === false) {
|
||||
deactivate()
|
||||
}
|
||||
}
|
||||
if (thisObject.visible === true) {
|
||||
let textAreaDiv = document.getElementById('textAreaDiv')
|
||||
textAreaDiv.style = 'position:fixed; top:' + textAreaPosition.y + 'px; left:' + textAreaPosition.x + 'px; z-index:1; '
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
if (thisObject.visible === true) {
|
||||
let position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
position = thisObject.container.frame.frameThisPoint(position)
|
||||
|
||||
let radius = thisObject.container.frame.radius * SIZE_FACTOR
|
||||
|
||||
if (radius > 0.5) {
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(position.x, position.y, radius * 1.3 + 3, 0, Math.PI * 2, true)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + 1 + ')'
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(position.x, position.y, radius * 1.3, 0, Math.PI * 2, true)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + 1 + ')'
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
function drawForeground () {
|
||||
let iconPosition = {
|
||||
x: 0,
|
||||
y: thisObject.currentRadius * 1.5
|
||||
}
|
||||
|
||||
iconPosition = thisObject.container.frame.frameThisPoint(iconPosition)
|
||||
|
||||
/* Menu Item */
|
||||
|
||||
if (thisObject.canDrawIcon === true && thisObject.currentRadius > 1) {
|
||||
browserCanvasContext.drawImage(thisObject.icon, iconPosition.x - thisObject.currentRadius, iconPosition.y - thisObject.currentRadius, thisObject.currentRadius * 2, thisObject.currentRadius * 2)
|
||||
|
||||
/* Menu Label */
|
||||
|
||||
let label = 'Code looks good!'
|
||||
let labelPoint
|
||||
let fontSize = 10
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
if (label !== undefined && thisObject.currentRadius >= thisObject.targetRadius) {
|
||||
labelPoint = {
|
||||
x: iconPosition.x + thisObject.currentRadius + 10,
|
||||
y: iconPosition.y + fontSize * FONT_ASPECT_RATIO
|
||||
}
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,733 @@
|
||||
/*
|
||||
This module represent one layer of many possible layers that could be at the Floating Space. Objects in the same layer are subject to
|
||||
the same physics, and by being at the same level, they interact with each other. For example, they can bounce when one hit the other.
|
||||
This module posesses its own physics engine. There are different types of floating objects, but this layer takes care of each of them.
|
||||
|
||||
Floating Objects Types:
|
||||
|
||||
1. Profile Balls: These are small balls with a profile picture, and two levels, one above and one below the ball. They are usually used
|
||||
to specify the location of a bot instance.
|
||||
|
||||
2. Notes: Notes are a rectangular area where some text is posted. It has a subject and a body.
|
||||
|
||||
3. UI Objects: These are small balls that represent parts of an strategy.
|
||||
*/
|
||||
|
||||
function newFloatingLayer () {
|
||||
const MODULE_NAME = 'Floating Layer'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
addFloatingObject: addFloatingObject,
|
||||
removeFloatingObject: removeFloatingObject,
|
||||
getFloatingObject: getFloatingObject,
|
||||
physics: physics,
|
||||
changeTargetRepulsion: changeTargetRepulsion,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
/*
|
||||
When objects are added to this layer they are added to the invisibleFloatingObjects array and they stay there because they are invisible.
|
||||
Visibility of objects is not decided at this layer, but outside. The ones who decides which object is visible or not are plotters, because
|
||||
they know the position of objects and the coordinate system which these positions belong to.
|
||||
|
||||
To save resources, the physics engine only apply its calculations to visible floating objects.
|
||||
*/
|
||||
|
||||
let invisibleFloatingObjects = []
|
||||
|
||||
/*
|
||||
Floating objects are accesible to plotters because they carry a "payload" object that was creted by them. This object includes the position
|
||||
and visible property. When visible turns to true, this layer module will move the reference of the floating object from the invisible array
|
||||
to the visibleFloatingObjects array. If the plotter decides that the object is to far from the what the user is seeing, it can eventually
|
||||
turn the visible property off again, forcing the layer to remove the object from the visibleFloatingObjects and place it again at the invisible
|
||||
array.
|
||||
*/
|
||||
|
||||
let visibleFloatingObjects = []
|
||||
|
||||
let maxTargetRepulsionForce = 0.001
|
||||
let currentHandle = 0
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
invisibleFloatingObjects = []
|
||||
visibleFloatingObjects = []
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
|
||||
/* We dont need to initialize anything right now. */
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
container = floatingObject.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
}
|
||||
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[i]
|
||||
container = floatingObject.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
}
|
||||
|
||||
return container
|
||||
}
|
||||
|
||||
function addFloatingObject (pFloatingObject) {
|
||||
currentHandle++
|
||||
pFloatingObject.handle = currentHandle // Math.floor((Math.random() * 10000000) + 1);
|
||||
visibleFloatingObjects.push(pFloatingObject)
|
||||
}
|
||||
|
||||
function removeFloatingObject (pFloatingObjectHandle) {
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[i]
|
||||
|
||||
if (floatingObject.handle === pFloatingObjectHandle) {
|
||||
invisibleFloatingObjects.splice(i, 1) // Delete item from array.
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
|
||||
if (floatingObject.handle === pFloatingObjectHandle) {
|
||||
visibleFloatingObjects.splice(i, 1) // Delete item from array.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getFloatingObject (pFloatingObjectHandle, pFloatingObjectIndex) {
|
||||
/*
|
||||
There are two ways to look for a floating object: by handle or by index. The search by handle is done on the visible and invisible array.
|
||||
The search by index is done only on the visible ones. The search by index is usually needed when mouse events occurs. In those cases
|
||||
only objects visible to the end usser matters.
|
||||
*/
|
||||
|
||||
try {
|
||||
if (pFloatingObjectHandle !== undefined) {
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[i]
|
||||
|
||||
if (floatingObject.handle === pFloatingObjectHandle) {
|
||||
return floatingObject
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
|
||||
if (floatingObject.handle === pFloatingObjectHandle) {
|
||||
return floatingObject
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pFloatingObjectIndex !== undefined) {
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
|
||||
if (i === pFloatingObjectIndex) {
|
||||
return floatingObject
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] getFloatingObject -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
drawInvisibleObjects()
|
||||
drawVisibleObjects()
|
||||
}
|
||||
|
||||
function drawInvisibleObjects () {
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[i]
|
||||
floatingObject.drawBackground()
|
||||
}
|
||||
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[i]
|
||||
floatingObject.drawMiddleground()
|
||||
}
|
||||
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[invisibleFloatingObjects.length - i - 1]
|
||||
floatingObject.drawForeground()
|
||||
}
|
||||
}
|
||||
|
||||
function drawVisibleObjects () {
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
floatingObject.drawBackground()
|
||||
}
|
||||
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
floatingObject.drawMiddleground()
|
||||
}
|
||||
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[visibleFloatingObjects.length - i - 1]
|
||||
floatingObject.drawForeground()
|
||||
}
|
||||
|
||||
/* Invisible objects on focus (freezed) should still have some priority */
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[i]
|
||||
floatingObject.drawOnFocus()
|
||||
}
|
||||
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
floatingObject.drawOnFocus()
|
||||
}
|
||||
}
|
||||
|
||||
function makeVisible () {
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[i]
|
||||
|
||||
if (floatingObject.isFrozen === false && floatingObject.isParentCollapsed === false) {
|
||||
visibleFloatingObjects.push(floatingObject)
|
||||
invisibleFloatingObjects.splice(i, 1) // Delete item from array.
|
||||
makeVisible()
|
||||
return // Only one at the time.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function makeInvisible () {
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
|
||||
if (floatingObject.isFrozen === true || floatingObject.isParentCollapsed === true) {
|
||||
invisibleFloatingObjects.push(floatingObject)
|
||||
visibleFloatingObjects.splice(i, 1) // Delete item from array.
|
||||
makeInvisible()
|
||||
return // Only one at the time.
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/******************************************/
|
||||
/* */
|
||||
/* Physics Engine Follows */
|
||||
/* */
|
||||
/******************************************/
|
||||
|
||||
function physics () {
|
||||
/*
|
||||
The Physics engine is hooked at the animation loop, so it executes very very often. According to this Physics engine, each
|
||||
floating object has a position and a speed. In fact, each floating object has two positions at the same time:
|
||||
|
||||
1. The current position: is the position of the ball, floating around.
|
||||
2. The anchor position: is the position of where the ball is achored on the plot. This one is inside the payload object controlled by the
|
||||
plotter.
|
||||
|
||||
The anchor position influences the current position, since the Physics engine will apply a gravity force to the ball's position in order
|
||||
to make it gravitate towards the anchor position. At the same time there will be a repulsion force that will prevent the ball reaching
|
||||
the anchor position, so that the anchor point keeps visible to the user.
|
||||
|
||||
*/
|
||||
|
||||
try {
|
||||
makeVisible()
|
||||
makeInvisible()
|
||||
applyPhysics()
|
||||
|
||||
function applyPhysics () {
|
||||
/* This function makes all the calculations to apply phisycs on all visible floatingObjects in this layer. */
|
||||
|
||||
try {
|
||||
for (let i = 0; i < invisibleFloatingObjects.length; i++) {
|
||||
let floatingObject = invisibleFloatingObjects[i]
|
||||
floatingObject.physics()
|
||||
}
|
||||
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
let floatingObject = visibleFloatingObjects[i]
|
||||
floatingObject.physics()
|
||||
|
||||
/* From here on, only if they are not too far. */
|
||||
if (canvas.floatingSpace.isItFar(floatingObject.payload)) { continue }
|
||||
|
||||
if (floatingObject.positionLocked === false) {
|
||||
floatingObject.container.frame.position.x = floatingObject.container.frame.position.x + floatingObject.currentSpeed.x
|
||||
floatingObject.container.frame.position.y = floatingObject.container.frame.position.y + floatingObject.currentSpeed.y
|
||||
}
|
||||
|
||||
if (floatingObject.friction < floatingObject.targetFriction) {
|
||||
floatingObject.friction = floatingObject.friction + 0.00001
|
||||
} else {
|
||||
floatingObject.friction = floatingObject.friction - 0.00001
|
||||
}
|
||||
|
||||
floatingObject.currentSpeed.x = floatingObject.currentSpeed.x * floatingObject.friction // Desaceleration factor.
|
||||
floatingObject.currentSpeed.y = floatingObject.currentSpeed.y * floatingObject.friction // Desaceleration factor.
|
||||
|
||||
let payload = {
|
||||
position: undefined,
|
||||
visible: false
|
||||
}
|
||||
|
||||
switch (floatingObject.type) {
|
||||
case 'UI Object': {
|
||||
payload.targetPosition = floatingObject.payload.targetPosition
|
||||
payload.visible = floatingObject.payload.visible
|
||||
break
|
||||
}
|
||||
default: {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (floatingObject.container.frame.position.x < payload.targetPosition.x) {
|
||||
floatingObject.currentSpeed.x = floatingObject.currentSpeed.x + 0.005
|
||||
} else {
|
||||
floatingObject.currentSpeed.x = floatingObject.currentSpeed.x - 0.005
|
||||
}
|
||||
|
||||
if (floatingObject.container.frame.position.y < payload.targetPosition.y) {
|
||||
floatingObject.currentSpeed.y = floatingObject.currentSpeed.y + 0.005
|
||||
} else {
|
||||
floatingObject.currentSpeed.y = floatingObject.currentSpeed.y - 0.005
|
||||
}
|
||||
|
||||
const MAX_SPEED = 10
|
||||
|
||||
if (floatingObject.currentSpeed.x > MAX_SPEED) {
|
||||
floatingObject.currentSpeed.x = MAX_SPEED
|
||||
}
|
||||
|
||||
if (floatingObject.currentSpeed.y > MAX_SPEED) {
|
||||
floatingObject.currentSpeed.y = MAX_SPEED
|
||||
}
|
||||
|
||||
if (floatingObject.currentSpeed.x < -MAX_SPEED) {
|
||||
floatingObject.currentSpeed.x = -MAX_SPEED
|
||||
}
|
||||
|
||||
if (floatingObject.currentSpeed.y < -MAX_SPEED) {
|
||||
floatingObject.currentSpeed.y = -MAX_SPEED
|
||||
}
|
||||
|
||||
// We let the Floating Object animate the physics loops by itself.
|
||||
checkBoundaries(floatingObject)
|
||||
|
||||
/* Collision Control */
|
||||
|
||||
for (let k = i + 1; k < visibleFloatingObjects.length; k++) {
|
||||
if (colliding(visibleFloatingObjects[i], visibleFloatingObjects[k])) {
|
||||
resolveCollision(visibleFloatingObjects[k], visibleFloatingObjects[i])
|
||||
}
|
||||
}
|
||||
|
||||
/* Calculate repulsion force produced by all other floatingObjects */
|
||||
|
||||
repulsionForceBetweenFloatingObjects(i)
|
||||
|
||||
targetRepulsionForce(i)
|
||||
|
||||
gravityForce(floatingObject, payload)
|
||||
|
||||
proximityBetweenFloatingObjects(i)
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] physics -> applyPhysics -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] physics -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function checkBoundaries (floatingObject) {
|
||||
if (floatingObject.container.frame.position.x - floatingObject.container.frame.radius < 0) {
|
||||
floatingObject.container.frame.position.x = floatingObject.container.frame.radius
|
||||
floatingObject.currentSpeed.x = -floatingObject.currentSpeed.x
|
||||
}
|
||||
|
||||
if (floatingObject.container.frame.position.x + floatingObject.container.frame.radius > canvas.floatingSpace.container.frame.width) {
|
||||
floatingObject.container.frame.position.x = canvas.floatingSpace.container.frame.width - floatingObject.container.frame.radius
|
||||
floatingObject.currentSpeed.x = -floatingObject.currentSpeed.x
|
||||
}
|
||||
|
||||
if (floatingObject.container.frame.position.y - floatingObject.container.frame.radius < 0) {
|
||||
floatingObject.container.frame.position.y = floatingObject.container.frame.radius
|
||||
floatingObject.currentSpeed.y = -floatingObject.currentSpeed.y
|
||||
}
|
||||
|
||||
if (floatingObject.container.frame.position.y + floatingObject.container.frame.radius > canvas.floatingSpace.container.frame.height) {
|
||||
floatingObject.container.frame.position.y = canvas.floatingSpace.container.frame.height - floatingObject.container.frame.radius
|
||||
floatingObject.currentSpeed.y = -floatingObject.currentSpeed.y
|
||||
}
|
||||
}
|
||||
|
||||
function gravityForce (floatingObject, payload) {
|
||||
try {
|
||||
/* We simulate a kind of gravity towards the target point of each floatingObject. This force will make the floatingObject to keep pushing to reach that point. */
|
||||
|
||||
const coulomb = 0.00001
|
||||
const minForce = 0.01
|
||||
|
||||
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 ...
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
let pos1 = {
|
||||
x: floatingObject.container.frame.position.x,
|
||||
y: floatingObject.container.frame.position.y
|
||||
}
|
||||
|
||||
let pos2 = {
|
||||
x: payload.targetPosition.x,
|
||||
y: payload.targetPosition.y
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
let forceVector = {
|
||||
x: unitVector.x * force,
|
||||
y: unitVector.y * force
|
||||
}
|
||||
|
||||
/* We add the force vector to the speed vector */
|
||||
if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) {
|
||||
floatingObject.currentSpeed.x = floatingObject.currentSpeed.x + forceVector.x
|
||||
floatingObject.currentSpeed.y = floatingObject.currentSpeed.y + forceVector.y
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] gravityForce -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function repulsionForceBetweenFloatingObjects (currentFloatingObject) {
|
||||
try {
|
||||
/* We generate a repulsion force between floatingObjects, that prevents them to be collisioning so often. */
|
||||
|
||||
const coulomb = 2
|
||||
|
||||
let floatingObject1 = visibleFloatingObjects[currentFloatingObject]
|
||||
|
||||
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.
|
||||
|
||||
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 ...
|
||||
|
||||
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. */
|
||||
|
||||
if (force > 1) {
|
||||
force = 1
|
||||
}
|
||||
|
||||
let pos1 = {
|
||||
x: floatingObject1.container.frame.position.x,
|
||||
y: floatingObject1.container.frame.position.y
|
||||
}
|
||||
|
||||
let pos2 = {
|
||||
x: floatingObject2.container.frame.position.x,
|
||||
y: floatingObject2.container.frame.position.y
|
||||
}
|
||||
|
||||
let posDiff = { // Next we need the vector resulting from the 2 positions.
|
||||
x: pos2.x - pos1.x,
|
||||
y: pos2.y - pos1.y
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
let forceVector = {
|
||||
x: unitVector.x * force,
|
||||
y: unitVector.y * force
|
||||
}
|
||||
|
||||
/* We substract the force vector to the speed vector of the current floatingObject */
|
||||
if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) {
|
||||
floatingObject1.currentSpeed.x = floatingObject1.currentSpeed.x - forceVector.x
|
||||
floatingObject1.currentSpeed.y = floatingObject1.currentSpeed.y - forceVector.y
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] repulsionForceBetweenFloatingObjects -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
let floatingObject1 = visibleFloatingObjects[currentFloatingObject]
|
||||
|
||||
for (let i = 0; i < visibleFloatingObjects.length; i++) {
|
||||
// The force to be applied is considering all other floatingObjects...
|
||||
|
||||
let floatingObject2 = visibleFloatingObjects[i] // So, for each floatingObject...
|
||||
|
||||
let payload = {
|
||||
position: undefined
|
||||
}
|
||||
|
||||
switch (floatingObject2.type) {
|
||||
case 'UI Object': {
|
||||
payload.targetPosition = floatingObject2.payload.targetPosition
|
||||
break
|
||||
}
|
||||
default: {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
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 ...
|
||||
|
||||
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. */
|
||||
|
||||
if (force > maxTargetRepulsionForce) {
|
||||
force = maxTargetRepulsionForce
|
||||
}
|
||||
|
||||
let pos1 = {
|
||||
x: floatingObject1.container.frame.position.x,
|
||||
y: floatingObject1.container.frame.position.y
|
||||
}
|
||||
|
||||
let pos2 = {
|
||||
x: payload.targetPosition.x,
|
||||
y: payload.targetPosition.y
|
||||
}
|
||||
|
||||
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) {
|
||||
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
|
||||
}
|
||||
|
||||
let forceVector = {
|
||||
x: unitVector.x * force,
|
||||
y: unitVector.y * force
|
||||
}
|
||||
|
||||
/* We substract the force vector to the speed vector of the current floatingObject */
|
||||
|
||||
if (isNaN(forceVector.x) === false && isNaN(forceVector.y) === false) {
|
||||
floatingObject1.currentSpeed.x = floatingObject1.currentSpeed.x - forceVector.x
|
||||
floatingObject1.currentSpeed.y = floatingObject1.currentSpeed.y - forceVector.y
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] targetRepulsionForce -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function changeTargetRepulsion (pDelta) {
|
||||
try {
|
||||
if (pDelta > 0) {
|
||||
pDelta = 1
|
||||
} else {
|
||||
pDelta = -1
|
||||
}
|
||||
|
||||
maxTargetRepulsionForce = maxTargetRepulsionForce + pDelta / 1000
|
||||
|
||||
if (maxTargetRepulsionForce < 0.0000000001) {
|
||||
maxTargetRepulsionForce = 0.0000000001
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] changeTargetRepulsion -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function colliding (floatingObject1, floatingObject2) {
|
||||
try {
|
||||
/* This function detects weather 2 floatingObjects collide with each other. */
|
||||
|
||||
let r1 = floatingObject1.container.frame.radius
|
||||
let r2 = floatingObject2.container.frame.radius
|
||||
|
||||
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.
|
||||
return false
|
||||
} else if (distance <= r1 + r2) {
|
||||
// One circle contains the other.
|
||||
return true
|
||||
} else if ((distance === 0) && (r1 === r2)) {
|
||||
// the circles coincide.
|
||||
return true
|
||||
} else return true
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] colliding -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function distance (x1, y1, x2, y2) {
|
||||
return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2))
|
||||
}
|
||||
|
||||
function resolveCollision (floatingObject1, floatingObject2) {
|
||||
try {
|
||||
/* This function changes speed and position of floatingObjects that are in collision */
|
||||
|
||||
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))
|
||||
|
||||
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
|
||||
|
||||
let direction_1 = Math.atan2(floatingObject1.currentSpeed.y, floatingObject1.currentSpeed.x)
|
||||
let direction_2 = Math.atan2(floatingObject2.currentSpeed.y, floatingObject2.currentSpeed.x)
|
||||
|
||||
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)
|
||||
|
||||
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
|
||||
|
||||
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
|
||||
|
||||
floatingObject2.currentSpeed.x = cosAngle * final_xspeed_2 - sinAngle * final_yspeed_2
|
||||
floatingObject2.currentSpeed.y = sinAngle * final_xspeed_2 + cosAngle * final_yspeed_2
|
||||
|
||||
let pos1 = {
|
||||
x: floatingObject1.container.frame.position.x,
|
||||
y: floatingObject1.container.frame.position.y
|
||||
}
|
||||
|
||||
let pos2 = {
|
||||
x: floatingObject2.container.frame.position.x,
|
||||
y: floatingObject2.container.frame.position.y
|
||||
}
|
||||
|
||||
// get the mtd
|
||||
let posDiff = {
|
||||
x: pos1.x - pos2.x,
|
||||
y: pos1.y - pos2.y
|
||||
}
|
||||
|
||||
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
|
||||
let scalar = (((floatingObject1.container.frame.radius + floatingObject2.container.frame.radius) - d) / d)
|
||||
|
||||
let minTD = {
|
||||
x: posDiff.x * scalar,
|
||||
y: posDiff.y * scalar
|
||||
}
|
||||
|
||||
// resolve intersection --
|
||||
// computing inverse mass quantities
|
||||
let im1 = 1 / floatingObject1.currentMass
|
||||
let im2 = 1 / floatingObject2.currentMass
|
||||
|
||||
// push-pull them apart based off their mass
|
||||
|
||||
pos1.x = pos1.x + minTD.x * (im1 / (im1 + im2))
|
||||
pos1.y = pos1.y + minTD.y * (im1 / (im1 + im2))
|
||||
pos2.x = pos2.x - minTD.x * (im2 / (im1 + im2))
|
||||
pos2.y = pos2.y - minTD.y * (im2 / (im1 + im2))
|
||||
|
||||
if (floatingObject1.positionLocked === false && isNaN(pos1.x) === false && isNaN(pos1.y) === false) {
|
||||
floatingObject1.container.frame.position = pos1
|
||||
}
|
||||
if (floatingObject2.positionLocked === false && isNaN(pos2.x) === false && isNaN(pos2.y) === false) {
|
||||
floatingObject2.container.frame.position = pos2
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] resolveCollision -> err= ' + err.stack) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,566 @@
|
||||
|
||||
function newFloatingObject () {
|
||||
const MODULE_NAME = 'Floating Object'
|
||||
const ERROR_LOG = true
|
||||
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
fitFunction: undefined,
|
||||
container: undefined,
|
||||
positionLocked: false,
|
||||
isOnFocus: false,
|
||||
payload: undefined, // This is a reference to an object controlled by a Plotter. The plotter can change its internal value and we will see them from here.
|
||||
type: undefined, // Currently there are two types of Floating Objects: Profile Balls, and Notes.
|
||||
currentSpeed: 0, // This is the current speed of the floating object.
|
||||
currentMass: 0, // This is the current mass of the floating object, including its zoom applied.
|
||||
friction: 0, // This is a factor that will ultimatelly desacelerate the floating object.
|
||||
targetFriction: 0,
|
||||
rawMass: 0, // This is the mass value without zoom.
|
||||
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,
|
||||
isFrozen: false,
|
||||
angleToParent: ANGLE_TO_PARENT.RANGE_90,
|
||||
distanceToParent: DISTANCE_TO_PARENT.PARENT_100X,
|
||||
isCollapsed: false,
|
||||
isParentCollapsed: false,
|
||||
frozenManually: false,
|
||||
collapsedManually: false,
|
||||
getPinStatus: getPinStatus,
|
||||
getFreezeStatus: getFreezeStatus,
|
||||
getCollapseStatus: getCollapseStatus,
|
||||
getAngleToParent: getAngleToParent,
|
||||
getDistanceToParent: getDistanceToParent,
|
||||
nearbyFloatingObjects: [],
|
||||
setPosition: setPosition,
|
||||
pinToggle: pinToggle,
|
||||
freezeToggle: freezeToggle,
|
||||
collapseToggle: collapseToggle,
|
||||
angleToParentToggle: angleToParentToggle,
|
||||
distanceToParentToggle: distanceToParentToggle,
|
||||
physics: physics,
|
||||
initializeMass: initializeMass,
|
||||
initializeRadius: initializeRadius,
|
||||
initializeImageSize: initializeImageSize,
|
||||
initializeFontSize: initializeFontSize,
|
||||
initializeCurrentPosition: initializeCurrentPosition,
|
||||
radomizeCurrentSpeed: radomizeCurrentSpeed,
|
||||
drawBackground: drawBackground,
|
||||
drawMiddleground: drawMiddleground,
|
||||
drawForeground: drawForeground,
|
||||
drawOnFocus: drawOnFocus,
|
||||
updateMass: updateMass, // Function to update the mass when the zoom level changed.
|
||||
updateRadius: updateRadius, // Function to update the radius when the zoom level changed.
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME, 'Circle')
|
||||
thisObject.container.isClickeable = true
|
||||
thisObject.container.isDraggeable = true
|
||||
thisObject.container.detectMouseOver = true
|
||||
thisObject.container.frame.radius = 0
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
let selfMouseOverEventSubscriptionId
|
||||
let selfMouseClickEventSubscriptionId
|
||||
let spaceMouseOverEventSubscriptionId
|
||||
let spaceFocusAquiredEventSubscriptionId
|
||||
let lastParentAngle
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(selfMouseOverEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId)
|
||||
canvas.floatingSpace.container.eventHandler.stopListening(spaceMouseOverEventSubscriptionId)
|
||||
canvas.floatingSpace.container.eventHandler.stopListening(spaceFocusAquiredEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.nearbyFloatingObjects = undefined
|
||||
}
|
||||
|
||||
function initialize (type, payload) {
|
||||
thisObject.payload = payload
|
||||
thisObject.type = type
|
||||
|
||||
selfMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
|
||||
|
||||
/* To consider that this object lost the focus, we monitor the space for 2 key events */
|
||||
spaceMouseOverEventSubscriptionId = canvas.floatingSpace.container.eventHandler.listenToEvent('onMouseOver', mouseOverFlotingSpace)
|
||||
spaceFocusAquiredEventSubscriptionId = canvas.floatingSpace.container.eventHandler.listenToEvent('onFocusAquired', someoneAquiredFocus)
|
||||
|
||||
/* Assign a position and speed */
|
||||
|
||||
thisObject.initializeCurrentPosition(thisObject.payload.targetPosition)
|
||||
thisObject.radomizeCurrentSpeed()
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if ((thisObject.isCollapsed === true && thisObject.collapsedManually === false) || thisObject.isParentCollapsed === true) { return }
|
||||
let container
|
||||
|
||||
container = thisObject.payload.uiObject.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
|
||||
if (thisObject.container.frame.isThisScreenPointHere(point) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function getPinStatus () {
|
||||
return thisObject.isPinned
|
||||
}
|
||||
|
||||
function pinToggle () {
|
||||
if (thisObject.isPinned !== true) {
|
||||
thisObject.isPinned = true
|
||||
thisObject.positionLocked = true
|
||||
} else {
|
||||
thisObject.isPinned = false
|
||||
}
|
||||
return thisObject.isPinned
|
||||
}
|
||||
|
||||
function getFreezeStatus () {
|
||||
return thisObject.isFrozen
|
||||
}
|
||||
|
||||
function getCollapseStatus () {
|
||||
return thisObject.isCollapsed
|
||||
}
|
||||
|
||||
function getAngleToParent () {
|
||||
return thisObject.angleToParent
|
||||
}
|
||||
|
||||
function getDistanceToParent () {
|
||||
return thisObject.distanceToParent
|
||||
}
|
||||
|
||||
function freezeToggle () {
|
||||
if (thisObject.isFrozen !== true) {
|
||||
thisObject.isFrozen = true
|
||||
thisObject.frozenManually = true
|
||||
} else {
|
||||
thisObject.isFrozen = false
|
||||
thisObject.frozenManually = false
|
||||
}
|
||||
return thisObject.isFrozen
|
||||
}
|
||||
|
||||
function collapseToggle () {
|
||||
if (thisObject.isCollapsed !== true) {
|
||||
thisObject.isCollapsed = true
|
||||
thisObject.collapsedManually = true
|
||||
} else {
|
||||
thisObject.isCollapsed = false
|
||||
thisObject.collapsedManually = false
|
||||
}
|
||||
return thisObject.isCollapsed
|
||||
}
|
||||
|
||||
function angleToParentToggle () {
|
||||
switch (thisObject.angleToParent) {
|
||||
case ANGLE_TO_PARENT.NOT_FIXED:
|
||||
thisObject.angleToParent = ANGLE_TO_PARENT.RANGE_360
|
||||
break
|
||||
case ANGLE_TO_PARENT.RANGE_360:
|
||||
thisObject.angleToParent = ANGLE_TO_PARENT.RANGE_180
|
||||
break
|
||||
case ANGLE_TO_PARENT.RANGE_180:
|
||||
thisObject.angleToParent = ANGLE_TO_PARENT.RANGE_90
|
||||
break
|
||||
case ANGLE_TO_PARENT.RANGE_90:
|
||||
thisObject.angleToParent = ANGLE_TO_PARENT.RANGE_45
|
||||
break
|
||||
case ANGLE_TO_PARENT.RANGE_45:
|
||||
thisObject.angleToParent = ANGLE_TO_PARENT.NOT_FIXED
|
||||
break
|
||||
}
|
||||
|
||||
return thisObject.angleToParent
|
||||
}
|
||||
|
||||
function distanceToParentToggle () {
|
||||
switch (thisObject.distanceToParent) {
|
||||
case DISTANCE_TO_PARENT.NOT_FIXED:
|
||||
thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_025X
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_025X:
|
||||
thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_050X
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_050X:
|
||||
thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_100X
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_100X:
|
||||
thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_150X
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_150X:
|
||||
thisObject.distanceToParent = DISTANCE_TO_PARENT.PARENT_200X
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_200X:
|
||||
thisObject.distanceToParent = DISTANCE_TO_PARENT.NOT_FIXED
|
||||
break
|
||||
}
|
||||
|
||||
return thisObject.distanceToParent
|
||||
}
|
||||
|
||||
function physics () {
|
||||
collapsePhysics()
|
||||
frozenPhysics()
|
||||
/* From here on, only if they are not too far. */
|
||||
if (canvas.floatingSpace.isItFar(thisObject.payload)) { return }
|
||||
thisObjectPhysics()
|
||||
thisObject.payload.uiObject.physics()
|
||||
positionContraintsPhysics()
|
||||
}
|
||||
|
||||
function frozenPhysics () {
|
||||
if (thisObject.frozenManually !== false) { return }
|
||||
if (thisObject.payload === undefined) { return }
|
||||
let parent = thisObject.payload.chainParent
|
||||
if (parent !== undefined) {
|
||||
if (parent.payload !== undefined) {
|
||||
if (parent.payload.floatingObject !== undefined) {
|
||||
thisObject.isFrozen = parent.payload.floatingObject.isFrozen
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function collapsePhysics () {
|
||||
let parent = thisObject.payload.chainParent
|
||||
if (parent === undefined) { return }
|
||||
if (parent.payload === undefined) { return }
|
||||
if (parent.payload.floatingObject === undefined) { return }
|
||||
|
||||
thisObject.isParentCollapsed = parent.payload.floatingObject.isCollapsed
|
||||
if (thisObject.collapsedManually === false) {
|
||||
thisObject.isCollapsed = parent.payload.floatingObject.isCollapsed
|
||||
}
|
||||
}
|
||||
|
||||
function positionContraintsPhysics () {
|
||||
if (thisObject.angleToParent !== ANGLE_TO_PARENT.NOT_FIXED && thisObject.isOnFocus !== true) {
|
||||
let parent = thisObject.payload.chainParent
|
||||
if (parent === undefined) { return }
|
||||
if (parent.payload === undefined) { return }
|
||||
if (parent.payload.position === undefined) { return }
|
||||
|
||||
let distanceToParent = Math.sqrt(Math.pow(parent.payload.position.x - thisObject.container.frame.position.x, 2) + Math.pow(parent.payload.position.y - thisObject.container.frame.position.y, 2)) // ... we calculate the distance ...
|
||||
let parentChildren = canvas.designSpace.workspace.nodeChildren.childrenCount(parent, thisObject.payload.node)
|
||||
let axisCount = parentChildren.childrenCount
|
||||
let axisIndex = parentChildren.childIndex
|
||||
let baseAngle = 0
|
||||
let angleToParentAngle
|
||||
|
||||
if (parent.payload.distance !== undefined) {
|
||||
switch (thisObject.distanceToParent) {
|
||||
case DISTANCE_TO_PARENT.PARENT_025X:
|
||||
distanceToParent = parent.payload.distance / 4
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_050X:
|
||||
distanceToParent = parent.payload.distance / 2
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_100X:
|
||||
distanceToParent = parent.payload.distance
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_150X:
|
||||
distanceToParent = parent.payload.distance * 1.5
|
||||
break
|
||||
case DISTANCE_TO_PARENT.PARENT_200X:
|
||||
distanceToParent = parent.payload.distance * 2
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
switch (thisObject.angleToParent) {
|
||||
case ANGLE_TO_PARENT.RANGE_360:
|
||||
angleToParentAngle = 360
|
||||
break
|
||||
case ANGLE_TO_PARENT.RANGE_180:
|
||||
angleToParentAngle = 180
|
||||
break
|
||||
case ANGLE_TO_PARENT.RANGE_90:
|
||||
angleToParentAngle = 90
|
||||
break
|
||||
case ANGLE_TO_PARENT.RANGE_45:
|
||||
angleToParentAngle = 45
|
||||
break
|
||||
}
|
||||
|
||||
if (axisIndex === undefined) {
|
||||
axisCount = 1
|
||||
axisIndex = axisCount
|
||||
}
|
||||
|
||||
if (parent.payload.chainParent !== undefined && parent.payload.angle !== undefined) {
|
||||
axisCount++
|
||||
axisIndex++
|
||||
baseAngle = parent.payload.angle + 180
|
||||
lastParentAngle = parent.payload.angle
|
||||
} else {
|
||||
if (lastParentAngle !== undefined) {
|
||||
axisCount++
|
||||
axisIndex++
|
||||
baseAngle = lastParentAngle + 180
|
||||
}
|
||||
}
|
||||
|
||||
let separatorAngle = (360 - angleToParentAngle) / 2
|
||||
let angleStep = angleToParentAngle / axisCount
|
||||
|
||||
thisObject.payload.angle = baseAngle + separatorAngle + (axisIndex - 1) * angleStep
|
||||
if (thisObject.payload.angle >= 360) {
|
||||
thisObject.payload.angle = thisObject.payload.angle - 360
|
||||
}
|
||||
|
||||
thisObject.payload.distance = distanceToParent
|
||||
|
||||
if (distanceToParent > 2000 || thisObject.isPinned === true) { return } // this is introduced to avoid edges cases when importing workspaces.
|
||||
|
||||
newPosition = {
|
||||
x: parent.payload.position.x + distanceToParent * Math.cos(toRadians(thisObject.payload.angle)),
|
||||
y: parent.payload.position.y + distanceToParent * Math.sin(toRadians(thisObject.payload.angle))
|
||||
}
|
||||
if (isNaN(newPosition.x) === false) {
|
||||
thisObject.container.frame.position.x = newPosition.x
|
||||
}
|
||||
if (isNaN(newPosition.y) === false) {
|
||||
thisObject.container.frame.position.y = newPosition.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function thisObjectPhysics () {
|
||||
// The radius also have a target.
|
||||
|
||||
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 + 15
|
||||
} else {
|
||||
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) >= 10) {
|
||||
if (thisObject.currentImageSize < thisObject.targetImageSize) {
|
||||
thisObject.currentImageSize = thisObject.currentImageSize + 10
|
||||
} else {
|
||||
thisObject.currentImageSize = thisObject.currentImageSize - 10
|
||||
}
|
||||
}
|
||||
|
||||
// The fontSize also have a target.
|
||||
|
||||
if (Math.abs(thisObject.currentFontSize - thisObject.targetFontSize) >= 1) {
|
||||
if (thisObject.currentFontSize < thisObject.targetFontSize) {
|
||||
thisObject.currentFontSize = thisObject.currentFontSize + 1
|
||||
} else {
|
||||
thisObject.currentFontSize = thisObject.currentFontSize - 1
|
||||
}
|
||||
}
|
||||
|
||||
/* Floating object position in screen coordinates */
|
||||
|
||||
thisObject.payload.position.x = thisObject.container.frame.position.x
|
||||
thisObject.payload.position.y = thisObject.container.frame.position.y
|
||||
}
|
||||
|
||||
function onMouseOver (point) {
|
||||
if (thisObject.isOnFocus === false) {
|
||||
thisObject.targetRadius = thisObject.rawRadius * 6.0
|
||||
thisObject.targetImageSize = thisObject.rawImageSize * 2.0
|
||||
thisObject.targetFontSize = thisObject.rawFontSize * 2.0
|
||||
|
||||
thisObject.payload.uiObject.container.eventHandler.raiseEvent('onFocus', point)
|
||||
|
||||
thisObject.positionLocked = true
|
||||
|
||||
canvas.floatingSpace.container.eventHandler.raiseEvent('onFocusAquired', thisObject.container)
|
||||
thisObject.isOnFocus = true
|
||||
}
|
||||
}
|
||||
|
||||
function mouseOverFlotingSpace (point) {
|
||||
removeFocus()
|
||||
}
|
||||
|
||||
function someoneAquiredFocus (container) {
|
||||
if (container === undefined) {
|
||||
return
|
||||
}
|
||||
if (thisObject.container === undefined) {
|
||||
return
|
||||
}
|
||||
if (container.id !== thisObject.container.id) {
|
||||
removeFocus()
|
||||
}
|
||||
}
|
||||
|
||||
function removeFocus () {
|
||||
if (thisObject.payload === undefined) { return }
|
||||
if (thisObject.isOnFocus === true) {
|
||||
thisObject.targetRadius = thisObject.rawRadius * 1
|
||||
thisObject.targetImageSize = thisObject.rawImageSize * 1
|
||||
thisObject.targetFontSize = thisObject.rawFontSize * 1
|
||||
|
||||
thisObject.payload.uiObject.container.eventHandler.raiseEvent('onNotFocus')
|
||||
|
||||
if (thisObject.isPinned !== true) {
|
||||
thisObject.positionLocked = false
|
||||
}
|
||||
thisObject.isOnFocus = false
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseClick (pPoint) {
|
||||
let event = {
|
||||
point: pPoint,
|
||||
parent: thisObject
|
||||
}
|
||||
thisObject.payload.uiObject.container.eventHandler.raiseEvent('onMouseClick', event)
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
if (canvas.floatingSpace.isItFar(thisObject.payload)) { return }
|
||||
if ((thisObject.isCollapsed === true && thisObject.collapsedManually === false) || thisObject.isParentCollapsed === true) { return }
|
||||
thisObject.payload.uiObject.drawBackground()
|
||||
}
|
||||
|
||||
function drawMiddleground () {
|
||||
if (canvas.floatingSpace.isItFar(thisObject.payload)) { return }
|
||||
if ((thisObject.isCollapsed === true && thisObject.collapsedManually === false) || thisObject.isParentCollapsed === true) { return }
|
||||
thisObject.payload.uiObject.drawMiddleground()
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
if (canvas.floatingSpace.isItFar(thisObject.payload)) { return }
|
||||
if ((thisObject.isCollapsed === true && thisObject.collapsedManually === false) || thisObject.isParentCollapsed === true) { return }
|
||||
thisObject.payload.uiObject.drawForeground()
|
||||
}
|
||||
|
||||
function drawOnFocus () {
|
||||
thisObject.payload.uiObject.drawOnFocus()
|
||||
}
|
||||
|
||||
function initializeMass (suggestedValue) {
|
||||
let mass = suggestedValue
|
||||
if (mass < 0.1) {
|
||||
mass = 0.1
|
||||
}
|
||||
|
||||
thisObject.rawMass = mass
|
||||
thisObject.currentMass = mass
|
||||
}
|
||||
|
||||
function initializeRadius (suggestedValue) {
|
||||
let radius = suggestedValue
|
||||
if (radius < 2) {
|
||||
radius = 2
|
||||
}
|
||||
|
||||
thisObject.rawRadius = radius
|
||||
thisObject.targetRadius = radius
|
||||
thisObject.container.frame.radius = radius / 3
|
||||
|
||||
thisObject.container.eventHandler.raiseEvent('Dimmensions Changed', event)
|
||||
}
|
||||
|
||||
function initializeImageSize (suggestedValue) {
|
||||
var size = suggestedValue
|
||||
if (size < 2) {
|
||||
size = 2
|
||||
}
|
||||
|
||||
thisObject.rawImageSize = size
|
||||
thisObject.targetImageSize = size
|
||||
thisObject.currentImageSize = size / 3
|
||||
}
|
||||
|
||||
function initializeFontSize (suggestedValue) {
|
||||
var size = suggestedValue
|
||||
if (size < 3) {
|
||||
size = 3
|
||||
}
|
||||
|
||||
thisObject.rawFontSize = size
|
||||
thisObject.targetFontSize = size
|
||||
thisObject.currentFontSize = size / 3
|
||||
}
|
||||
|
||||
function initializeCurrentPosition (arroundPoint) {
|
||||
let position = {}
|
||||
|
||||
if (thisObject.payload.position === undefined) {
|
||||
position = {
|
||||
x: Math.floor((Math.random() * (200) - 100)) + arroundPoint.x,
|
||||
y: Math.floor((Math.random() * (200) - 100)) + arroundPoint.y
|
||||
}
|
||||
} else {
|
||||
position = {
|
||||
x: thisObject.payload.position.x,
|
||||
y: thisObject.payload.position.y
|
||||
}
|
||||
}
|
||||
|
||||
setPosition(position)
|
||||
}
|
||||
|
||||
function setPosition (position) {
|
||||
thisObject.container.frame.position.x = position.x
|
||||
thisObject.container.frame.position.y = position.y
|
||||
|
||||
thisObject.payload.position = {}
|
||||
thisObject.payload.position.x = position.x
|
||||
thisObject.payload.position.y = position.y
|
||||
}
|
||||
|
||||
function radomizeCurrentSpeed () {
|
||||
var initialXDirection
|
||||
var randomX = Math.floor((Math.random() * 10) + 1)
|
||||
if (randomX > 5) {
|
||||
initialXDirection = 1
|
||||
} else {
|
||||
initialXDirection = -1
|
||||
}
|
||||
|
||||
var initialYDirection
|
||||
var randomY = Math.floor((Math.random() * 10) + 1)
|
||||
if (randomY > 5) {
|
||||
initialYDirection = 1
|
||||
} else {
|
||||
initialYDirection = -1
|
||||
}
|
||||
|
||||
var velocity = {
|
||||
x: initialXDirection * Math.floor((Math.random() * 300) + 1) / 100,
|
||||
y: initialYDirection * Math.floor((Math.random() * 300) + 1) / 100
|
||||
}
|
||||
|
||||
thisObject.currentSpeed = velocity
|
||||
}
|
||||
|
||||
function updateMass () {
|
||||
|
||||
}
|
||||
|
||||
function updateRadius () {
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,215 @@
|
||||
|
||||
function newFormulaEditor () {
|
||||
const MODULE_NAME = 'Formula Editor'
|
||||
|
||||
let thisObject = {
|
||||
isVisibleFunction: undefined,
|
||||
iconOK: undefined,
|
||||
iconNOT_OK: undefined,
|
||||
visible: false,
|
||||
imagePathOK: undefined,
|
||||
imagePathNOT_OK: undefined,
|
||||
rawRadius: undefined,
|
||||
targetRadius: undefined,
|
||||
currentRadius: undefined,
|
||||
container: undefined,
|
||||
payload: undefined,
|
||||
deactivate: deactivate,
|
||||
activate: activate,
|
||||
physics: physics,
|
||||
drawBackground: drawBackground,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.detectMouseOver = false
|
||||
thisObject.container.frame.radius = 0
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
let isMouseOver = false
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.iconOK = undefined
|
||||
thisObject.iconNOT_OK = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.isVisibleFunction = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
/* Load Images */
|
||||
|
||||
thisObject.iconOK = new Image()
|
||||
thisObject.iconOK.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
thisObject.iconNOT_OK = new Image()
|
||||
thisObject.iconNOT_OK.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
thisObject.canDrawIcon = true
|
||||
}
|
||||
thisObject.iconNOT_OK.src = window.canvasApp.urlPrefix + thisObject.imagePathNOT_OK
|
||||
}
|
||||
// thisObject.iconOK.src = window.canvasApp.urlPrefix + thisObject.imagePathOK
|
||||
// thisObject.icon = thisObject.iconOK // The default value is ON.
|
||||
}
|
||||
|
||||
function deactivate () {
|
||||
if (thisObject.visible === true) {
|
||||
thisObject.visible = false
|
||||
let textArea = document.getElementById('textArea')
|
||||
textArea.style.display = 'none'
|
||||
thisObject.payload.node.code = textArea.value
|
||||
}
|
||||
}
|
||||
|
||||
function activate (payload) {
|
||||
thisObject.visible = true
|
||||
thisObject.payload = payload
|
||||
thisObject.rawRadius = 8
|
||||
thisObject.targetRadius = thisObject.container.frame.radius
|
||||
thisObject.currentRadius = 0
|
||||
thisObject.payload.uiObject.setErrorMessage('', 0)
|
||||
|
||||
let textArea = document.getElementById('textArea')
|
||||
textArea.value = payload.node.code
|
||||
textArea.style = 'resize: none;' +
|
||||
' border: none;' +
|
||||
' outline: none;' +
|
||||
'box-shadow: none;' +
|
||||
'overflow:hidden;' +
|
||||
'font-family: ' + UI_FONT.PRIMARY + ';' +
|
||||
'font-size: 12px;' +
|
||||
'background-color: rgb(' + UI_COLOR.MANGANESE_PURPLE + ');' +
|
||||
'color:rgb(255, 255, 255);' +
|
||||
'width: ' + thisObject.container.frame.width + 'px;' +
|
||||
'height: ' + thisObject.container.frame.height + 'px'
|
||||
textArea.style.display = 'block'
|
||||
textArea.focus()
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
if (thisObject.visible === true) {
|
||||
if (point.x === VERY_LARGE_NUMBER) {
|
||||
/* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that
|
||||
anyone can react. In our case, the code editor has a text area that is not part of the canvas, so the event is
|
||||
triggered. We compensate recognizing this coordinate and returning our container. */
|
||||
return thisObject.container
|
||||
}
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true, false) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (Math.abs(thisObject.currentRadius - thisObject.targetRadius) >= 0.5) {
|
||||
if (thisObject.currentRadius < thisObject.targetRadius) {
|
||||
thisObject.currentRadius = thisObject.currentRadius + 0.5
|
||||
} else {
|
||||
thisObject.currentRadius = thisObject.currentRadius - 0.5
|
||||
}
|
||||
}
|
||||
|
||||
thisObject.container.frame.position.x = 0
|
||||
thisObject.container.frame.position.y = 0
|
||||
|
||||
thisObject.container.frame.width = thisObject.container.frame.radius * 1.8 * 2
|
||||
thisObject.container.frame.height = thisObject.container.frame.radius * 1 * 2
|
||||
|
||||
let textAreaPosition = {
|
||||
x: 0 - thisObject.container.frame.width / 2,
|
||||
y: 0 - thisObject.container.frame.height * 3 / 7 + CURRENT_TOP_MARGIN
|
||||
}
|
||||
|
||||
textAreaPosition = thisObject.container.frame.frameThisPoint(textAreaPosition)
|
||||
if (thisObject.visible === true) {
|
||||
let checkPosition = {
|
||||
x: textAreaPosition.x,
|
||||
y: textAreaPosition.y - CURRENT_TOP_MARGIN
|
||||
}
|
||||
if (thisObject.isVisibleFunction(checkPosition) === false) {
|
||||
deactivate()
|
||||
}
|
||||
}
|
||||
if (thisObject.visible === true) {
|
||||
let textAreaDiv = document.getElementById('textAreaDiv')
|
||||
textAreaDiv.style = 'position:fixed; top:' + textAreaPosition.y + 'px; left:' + textAreaPosition.x + 'px; z-index:1; '
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
if (thisObject.visible === true) {
|
||||
let position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
position = thisObject.container.frame.frameThisPoint(position)
|
||||
|
||||
let radius = thisObject.container.frame.radius * 2
|
||||
|
||||
if (radius > 0.5) {
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(position.x, position.y, radius * 1.3 + 3, 0, Math.PI * 2, true)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + 1 + ')'
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.arc(position.x, position.y, radius * 1.3, 0, Math.PI * 2, true)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', ' + 1 + ')'
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
}
|
||||
function drawForeground () {
|
||||
let iconPosition = {
|
||||
x: 0,
|
||||
y: thisObject.currentRadius * 1.5
|
||||
}
|
||||
|
||||
iconPosition = thisObject.container.frame.frameThisPoint(iconPosition)
|
||||
|
||||
/* Menu Item */
|
||||
|
||||
if (thisObject.canDrawIcon === true && thisObject.currentRadius > 1) {
|
||||
browserCanvasContext.drawImage(thisObject.icon, iconPosition.x - thisObject.currentRadius, iconPosition.y - thisObject.currentRadius, thisObject.currentRadius * 2, thisObject.currentRadius * 2)
|
||||
|
||||
/* Menu Label */
|
||||
|
||||
let label = 'Code looks good!'
|
||||
let labelPoint
|
||||
let fontSize = 10
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
if (label !== undefined && thisObject.currentRadius >= thisObject.targetRadius) {
|
||||
labelPoint = {
|
||||
x: iconPosition.x + thisObject.currentRadius + 10,
|
||||
y: iconPosition.y + fontSize * FONT_ASPECT_RATIO
|
||||
}
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,483 @@
|
||||
|
||||
function newUiObjectConstructor () {
|
||||
const MODULE_NAME = 'UI Object Constructor'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
createUiObject: createUiObject,
|
||||
destroyUiObject: destroyUiObject,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let floatingLayer
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
floatingLayer = undefined
|
||||
}
|
||||
|
||||
function initialize (pFloatingLayer) {
|
||||
floatingLayer = pFloatingLayer
|
||||
}
|
||||
|
||||
function createUiObject (userAddingNew, payload) {
|
||||
let floatingObject = newFloatingObject()
|
||||
floatingObject.fitFunction = canvas.floatingSpace.fitIntoVisibleArea
|
||||
floatingObject.container.connectToParent(canvas.floatingSpace.container, false, false, false, false, false, false, false, false)
|
||||
floatingObject.initialize('UI Object', payload)
|
||||
payload.floatingObject = floatingObject
|
||||
|
||||
/*
|
||||
When this object is created based on a backup, share or clone, we will have a savedPayload that we will use to set the initial properties.
|
||||
If it is a new object being created out of the user interface, we jusst continue with the construction process.
|
||||
*/
|
||||
if (userAddingNew === false && payload.node.type !== 'Workspace') {
|
||||
let position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
position = {
|
||||
x: payload.node.savedPayload.position.x,
|
||||
y: payload.node.savedPayload.position.y
|
||||
}
|
||||
|
||||
floatingObject.setPosition(position)
|
||||
payload.node.savedPayload.position = undefined
|
||||
if (payload.node.savedPayload.floatingObject.isPinned === true) {
|
||||
floatingObject.pinToggle()
|
||||
}
|
||||
if (payload.node.savedPayload.floatingObject.isFrozen === true) {
|
||||
floatingObject.freezeToggle()
|
||||
}
|
||||
if (payload.node.savedPayload.floatingObject.isCollapsed === true) {
|
||||
floatingObject.collapseToggle()
|
||||
}
|
||||
if (payload.node.savedPayload.floatingObject.angleToParent !== undefined) {
|
||||
floatingObject.angleToParent = payload.node.savedPayload.floatingObject.angleToParent
|
||||
}
|
||||
if (payload.node.savedPayload.floatingObject.distanceToParent !== undefined) {
|
||||
floatingObject.distanceToParent = payload.node.savedPayload.floatingObject.distanceToParent
|
||||
}
|
||||
}
|
||||
|
||||
/* For brand new objects being created directly by the user, we will make them inherit some properties from their parents. */
|
||||
if (userAddingNew === true) {
|
||||
floatingObject.angleToParent = payload.parentNode.payload.floatingObject.angleToParent
|
||||
floatingObject.distanceToParent = payload.parentNode.payload.floatingObject.distanceToParent
|
||||
}
|
||||
|
||||
let uiObject = newUiObject()
|
||||
payload.uiObject = uiObject
|
||||
uiObject.fitFunction = canvas.floatingSpace.fitIntoVisibleArea
|
||||
uiObject.isVisibleFunction = canvas.floatingSpace.isThisPointVisible
|
||||
let menuItemsInitialValues = getMenuItemsInitialValues(uiObject, floatingObject, payload)
|
||||
|
||||
for (let i = 0; i < APP_SCHEMA_ARRAY.length; i++) {
|
||||
let schemaNode = APP_SCHEMA_ARRAY[i]
|
||||
if (schemaNode.type === payload.node.type) {
|
||||
let menuItems = schemaNode.menuItems
|
||||
for (let j = 0; j < menuItems.length; j++) {
|
||||
let item = menuItems[j]
|
||||
item.angle = undefined
|
||||
if (item.action.indexOf('Add ') >= 0 && item.action.indexOf('Missing') >= 0) {
|
||||
item.action = 'Add Missing Children'
|
||||
}
|
||||
if (item.action.indexOf('Delete ') >= 0) {
|
||||
// item.action = 'Delete UI Object'
|
||||
}
|
||||
if (item.action.indexOf('Configure ') >= 0) {
|
||||
if (schemaNode.editors.config === true) {
|
||||
// item.actionFunction = 'uiObject.configEditor.activate'
|
||||
}
|
||||
}
|
||||
if (item.action.indexOf('Edit ') >= 0) {
|
||||
if (schemaNode.editors.code === true) {
|
||||
// item.actionFunction = 'uiObject.codeEditor.activate'
|
||||
}
|
||||
}
|
||||
if (item.actionFunction === undefined) {
|
||||
// item.actionFunction = 'payload.onMenuItemClick'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uiObject.initialize(payload, menuItemsInitialValues)
|
||||
uiObject.container.connectToParent(floatingObject.container, false, false, true, true, false, false, true, true, true, true, true)
|
||||
|
||||
setFloatingObjectBasicProperties(floatingObject, payload)
|
||||
|
||||
if (payload.node.savedPayload !== undefined) {
|
||||
if (payload.node.savedPayload.uiObject !== undefined) {
|
||||
payload.uiObject.shortcutKey = payload.node.savedPayload.uiObject.shortcutKey
|
||||
}
|
||||
}
|
||||
|
||||
floatingLayer.addFloatingObject(floatingObject)
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
function addLeftIcons (menuItemsInitialValues, floatingObject, isPersonalData) {
|
||||
menuItemsInitialValues.push(
|
||||
{
|
||||
action: 'Pin / Unpin',
|
||||
actionFunction: floatingObject.pinToggle,
|
||||
actionStatus: floatingObject.getPinStatus,
|
||||
currentStatus: false,
|
||||
label: undefined,
|
||||
visible: true,
|
||||
iconPathOn: 'menu-fix-pinned',
|
||||
iconPathOff: 'menu-fix-unpinned',
|
||||
rawRadius: 8,
|
||||
targetRadius: 0,
|
||||
currentRadius: 0,
|
||||
ring: 1
|
||||
}
|
||||
)
|
||||
menuItemsInitialValues.push(
|
||||
{
|
||||
action: 'Change Tension Level',
|
||||
actionFunction: floatingObject.angleToParentToggle,
|
||||
actionStatus: floatingObject.getAngleToParent,
|
||||
currentStatus: true,
|
||||
label: undefined,
|
||||
visible: true,
|
||||
icons: ['angle-to-parent-000', 'angle-to-parent-360', 'angle-to-parent-180', 'angle-to-parent-090', 'angle-to-parent-045'],
|
||||
rawRadius: 8,
|
||||
targetRadius: 0,
|
||||
currentRadius: 0,
|
||||
ring: 1
|
||||
}
|
||||
)
|
||||
menuItemsInitialValues.push(
|
||||
{
|
||||
action: 'change Distance to Paarent',
|
||||
actionFunction: floatingObject.distanceToParentToggle,
|
||||
actionStatus: floatingObject.getDistanceToParent,
|
||||
currentStatus: true,
|
||||
label: undefined,
|
||||
visible: true,
|
||||
icons: ['distance-to-parent-000', 'distance-to-parent-025', 'distance-to-parent-050', 'distance-to-parent-100', 'distance-to-parent-150', 'distance-to-parent-200'],
|
||||
rawRadius: 8,
|
||||
targetRadius: 0,
|
||||
currentRadius: 0,
|
||||
ring: 1
|
||||
}
|
||||
)
|
||||
menuItemsInitialValues.push(
|
||||
{
|
||||
action: 'Freeze / Unfreeze',
|
||||
actionFunction: floatingObject.freezeToggle,
|
||||
actionStatus: floatingObject.getFreezeStatus,
|
||||
currentStatus: true,
|
||||
label: undefined,
|
||||
visible: true,
|
||||
iconPathOn: 'menu-mobility-unfreeze',
|
||||
iconPathOff: 'menu-mobility-freeze',
|
||||
rawRadius: 8,
|
||||
targetRadius: 0,
|
||||
currentRadius: 0,
|
||||
ring: 1
|
||||
}
|
||||
)
|
||||
menuItemsInitialValues.push(
|
||||
{
|
||||
action: 'Collapse / Uncollapse',
|
||||
actionFunction: floatingObject.collapseToggle,
|
||||
actionStatus: floatingObject.getCollapseStatus,
|
||||
currentStatus: false,
|
||||
label: undefined,
|
||||
visible: true,
|
||||
iconPathOn: 'menu-tree-plus',
|
||||
iconPathOff: 'menu-tree-minus',
|
||||
rawRadius: 8,
|
||||
targetRadius: 0,
|
||||
currentRadius: 0,
|
||||
ring: 1
|
||||
}
|
||||
)
|
||||
menuItemsInitialValues.push(
|
||||
{
|
||||
action: 'Backup',
|
||||
actionFunction: floatingObject.payload.onMenuItemClick,
|
||||
label: undefined,
|
||||
visible: true,
|
||||
iconPathOn: 'menu-backup',
|
||||
iconPathOff: 'menu-backup',
|
||||
rawRadius: 8,
|
||||
targetRadius: 0,
|
||||
currentRadius: 0,
|
||||
ring: 2
|
||||
}
|
||||
)
|
||||
menuItemsInitialValues.push(
|
||||
{
|
||||
action: 'Clone',
|
||||
actionFunction: floatingObject.payload.onMenuItemClick,
|
||||
label: undefined,
|
||||
visible: true,
|
||||
iconPathOn: 'clone',
|
||||
iconPathOff: 'clone',
|
||||
rawRadius: 8,
|
||||
targetRadius: 0,
|
||||
currentRadius: 0,
|
||||
ring: 2
|
||||
}
|
||||
)
|
||||
if (isPersonalData !== true) {
|
||||
menuItemsInitialValues.push(
|
||||
{
|
||||
action: 'Share',
|
||||
actionFunction: floatingObject.payload.onMenuItemClick,
|
||||
label: undefined,
|
||||
visible: true,
|
||||
iconPathOn: 'menu-share',
|
||||
iconPathOff: 'menu-share',
|
||||
rawRadius: 8,
|
||||
targetRadius: 0,
|
||||
currentRadius: 0,
|
||||
ring: 2
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
function getMenuItemsInitialValues (uiObject, floatingObject, payload) {
|
||||
let menuItemsInitialValues = []
|
||||
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(payload.node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
if (nodeDefinition.editors !== undefined) {
|
||||
if (nodeDefinition.editors.config === true) {
|
||||
uiObject.configEditor = newConfigEditor()
|
||||
uiObject.configEditor.isVisibleFunction = uiObject.isVisibleFunction
|
||||
uiObject.configEditor.initialize()
|
||||
uiObject.configEditor.container.connectToParent(uiObject.container, false, false, true, true, false, false, false, false)
|
||||
}
|
||||
if (nodeDefinition.editors.code === true) {
|
||||
uiObject.codeEditor = newCodeEditor()
|
||||
uiObject.codeEditor.isVisibleFunction = uiObject.isVisibleFunction
|
||||
uiObject.codeEditor.initialize()
|
||||
uiObject.codeEditor.container.connectToParent(uiObject.container, false, false, true, true, false, false, false, false)
|
||||
}
|
||||
if (nodeDefinition.editors.formula === true) {
|
||||
uiObject.formulaEditor = newFormulaEditor()
|
||||
uiObject.formulaEditor.isVisibleFunction = uiObject.isVisibleFunction
|
||||
uiObject.formulaEditor.initialize()
|
||||
uiObject.formulaEditor.container.connectToParent(uiObject.container, false, false, true, true, false, false, false, false)
|
||||
}
|
||||
if (nodeDefinition.editors.condition === true) {
|
||||
uiObject.conditionEditor = newConditionEditor()
|
||||
uiObject.conditionEditor.isVisibleFunction = uiObject.isVisibleFunction
|
||||
uiObject.conditionEditor.initialize()
|
||||
uiObject.conditionEditor.container.connectToParent(uiObject.container, false, false, true, true, false, false, false, false)
|
||||
}
|
||||
}
|
||||
if (nodeDefinition.addLeftIcons === true) {
|
||||
addLeftIcons(menuItemsInitialValues, floatingObject, nodeDefinition.isPersonalData)
|
||||
}
|
||||
if (nodeDefinition.isPinned === true) {
|
||||
floatingObject.isPinned = true
|
||||
}
|
||||
if (nodeDefinition.positionLocked === true) {
|
||||
floatingObject.positionLocked = true
|
||||
}
|
||||
|
||||
for (let i = 0; i < nodeDefinition.menuItems.length; i++) {
|
||||
let menutItemDefinition = nodeDefinition.menuItems[i]
|
||||
let newMenuItem = JSON.parse(JSON.stringify(menutItemDefinition))
|
||||
|
||||
/* We need to reference the real function based on its name */
|
||||
if (menutItemDefinition.actionFunction !== undefined) {
|
||||
newMenuItem.actionFunction = eval(menutItemDefinition.actionFunction)
|
||||
}
|
||||
|
||||
/* Adding default values */
|
||||
if (newMenuItem.visible === undefined) {
|
||||
newMenuItem.visible = true
|
||||
}
|
||||
|
||||
if (newMenuItem.rawRadius === undefined) {
|
||||
newMenuItem.rawRadius = 8
|
||||
}
|
||||
|
||||
if (newMenuItem.targetRadius === undefined) {
|
||||
newMenuItem.targetRadius = 0
|
||||
}
|
||||
|
||||
if (newMenuItem.currentRadius === undefined) {
|
||||
newMenuItem.currentRadius = 0
|
||||
}
|
||||
|
||||
menuItemsInitialValues.push(newMenuItem)
|
||||
}
|
||||
} else {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] getMenuItemsInitialValues -> UI Object Type not Recognized -> type = ' + payload.node.type) }
|
||||
}
|
||||
|
||||
return menuItemsInitialValues
|
||||
}
|
||||
|
||||
function setFloatingObjectBasicProperties (floatingObject, payload) {
|
||||
const FRICTION = 0.95
|
||||
const INITIAL_FRICTION = 0.97
|
||||
const INITIAL_FONT_SIZE = 12
|
||||
|
||||
switch (payload.node.type) {
|
||||
case 'Workspace': {
|
||||
level_0()
|
||||
floatingObject.angleToParent = ANGLE_TO_PARENT.RANGE_360
|
||||
break
|
||||
}
|
||||
|
||||
default: {
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(payload.node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
switch (nodeDefinition.level) {
|
||||
case 0: {
|
||||
level_0()
|
||||
break
|
||||
}
|
||||
case 1: {
|
||||
level_1()
|
||||
break
|
||||
}
|
||||
case 2: {
|
||||
level_2()
|
||||
break
|
||||
}
|
||||
case 3: {
|
||||
level_3()
|
||||
break
|
||||
}
|
||||
case 4: {
|
||||
level_4()
|
||||
break
|
||||
}
|
||||
case 5: {
|
||||
level_5()
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function level_0 () {
|
||||
floatingObject.targetFriction = 0.93
|
||||
floatingObject.friction = INITIAL_FRICTION
|
||||
|
||||
floatingObject.initializeMass(500)
|
||||
floatingObject.initializeRadius(45)
|
||||
floatingObject.initializeImageSize(80)
|
||||
floatingObject.initializeFontSize(INITIAL_FONT_SIZE)
|
||||
|
||||
floatingObject.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
}
|
||||
function level_1 () {
|
||||
floatingObject.targetFriction = 0.94
|
||||
floatingObject.friction = INITIAL_FRICTION
|
||||
|
||||
floatingObject.initializeMass(600)
|
||||
floatingObject.initializeRadius(45)
|
||||
floatingObject.initializeImageSize(80)
|
||||
floatingObject.initializeFontSize(INITIAL_FONT_SIZE)
|
||||
|
||||
floatingObject.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
|
||||
if (payload.node.savedPayload === undefined) {
|
||||
// floatingObject.angleToParentToggle()
|
||||
}
|
||||
}
|
||||
function level_2 () {
|
||||
floatingObject.targetFriction = FRICTION
|
||||
floatingObject.friction = INITIAL_FRICTION
|
||||
|
||||
floatingObject.initializeMass(300)
|
||||
floatingObject.initializeRadius(40)
|
||||
floatingObject.initializeImageSize(70)
|
||||
floatingObject.initializeFontSize(INITIAL_FONT_SIZE)
|
||||
|
||||
floatingObject.fillStyle = 'rgba(' + UI_COLOR.GREEN + ', 1)'
|
||||
|
||||
if (payload.node.savedPayload === undefined) {
|
||||
// floatingObject.angleToParentToggle()
|
||||
}
|
||||
}
|
||||
function level_3 () {
|
||||
floatingObject.targetFriction = FRICTION
|
||||
floatingObject.friction = INITIAL_FRICTION
|
||||
|
||||
floatingObject.initializeMass(150)
|
||||
floatingObject.initializeRadius(35)
|
||||
floatingObject.initializeImageSize(60)
|
||||
floatingObject.initializeFontSize(INITIAL_FONT_SIZE)
|
||||
|
||||
floatingObject.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', 1)'
|
||||
|
||||
if (payload.node.savedPayload === undefined) {
|
||||
// floatingObject.angleToParentToggle()
|
||||
}
|
||||
}
|
||||
function level_4 () {
|
||||
floatingObject.targetFriction = FRICTION
|
||||
floatingObject.friction = INITIAL_FRICTION
|
||||
|
||||
floatingObject.initializeMass(75)
|
||||
floatingObject.initializeRadius(30)
|
||||
floatingObject.initializeImageSize(50)
|
||||
floatingObject.initializeFontSize(INITIAL_FONT_SIZE)
|
||||
|
||||
floatingObject.fillStyle = 'rgba(' + UI_COLOR.TITANIUM_YELLOW + ', 1)'
|
||||
|
||||
if (payload.node.savedPayload === undefined) {
|
||||
// floatingObject.angleToParentToggle()
|
||||
}
|
||||
}
|
||||
function level_5 () {
|
||||
floatingObject.targetFriction = FRICTION
|
||||
floatingObject.friction = INITIAL_FRICTION
|
||||
|
||||
floatingObject.initializeMass(50)
|
||||
floatingObject.initializeRadius(25)
|
||||
floatingObject.initializeImageSize(40)
|
||||
floatingObject.initializeFontSize(INITIAL_FONT_SIZE)
|
||||
|
||||
floatingObject.fillStyle = 'rgba(' + UI_COLOR.RED + ', 1)'
|
||||
}
|
||||
function level_6 () {
|
||||
floatingObject.targetFriction = FRICTION
|
||||
floatingObject.friction = INITIAL_FRICTION
|
||||
|
||||
floatingObject.initializeMass(25)
|
||||
floatingObject.initializeRadius(20)
|
||||
floatingObject.initializeImageSize(30)
|
||||
floatingObject.initializeFontSize(INITIAL_FONT_SIZE)
|
||||
|
||||
floatingObject.fillStyle = 'rgba(' + UI_COLOR.RED + ', 1)'
|
||||
}
|
||||
|
||||
floatingObject.labelStrokeStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
}
|
||||
|
||||
function destroyUiObject (payload) {
|
||||
floatingLayer.removeFloatingObject(payload.floatingObject.handle)
|
||||
|
||||
payload.floatingObject.finalize()
|
||||
payload.floatingObject = undefined
|
||||
|
||||
payload.uiObject.finalize()
|
||||
payload.uiObject = undefined
|
||||
|
||||
payload.referenceParent = undefined
|
||||
payload.parent = undefined
|
||||
payload.chainParent = undefined
|
||||
payload.node.savedPayload = undefined
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,298 @@
|
||||
|
||||
function newUiObjectTitle () {
|
||||
const MODULE_NAME = 'UI Object Title'
|
||||
const ERROR_LOG = true
|
||||
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
isVisibleFunction: undefined,
|
||||
allwaysVisible: undefined,
|
||||
editMode: undefined,
|
||||
container: undefined,
|
||||
payload: undefined,
|
||||
enterEditMode: enterEditMode,
|
||||
exitEditMode: exitEditMode,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.isClickeable = true
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.frame.radius = 0
|
||||
|
||||
let selfFocusEventSubscriptionId
|
||||
let selfNotFocuskEventSubscriptionId
|
||||
let selfMouseClickEventSubscriptionId
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(selfFocusEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(selfNotFocuskEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.isVisibleFunction = undefined
|
||||
}
|
||||
|
||||
function initialize (payload) {
|
||||
thisObject.payload = payload
|
||||
|
||||
thisObject.allwaysVisible = false // Default value
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type)
|
||||
if (nodeDefinition !== undefined) {
|
||||
if (nodeDefinition.isTitleAllwaysVisible === true) {
|
||||
thisObject.allwaysVisible = true
|
||||
}
|
||||
}
|
||||
|
||||
selfFocusEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onFocus', onFocus)
|
||||
selfNotFocuskEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onNotFocus', onNotFocus)
|
||||
selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if (AT_FULL_SCREEN_MODE === true) { return } // Fullscreen mode does not allow INPUT elements
|
||||
|
||||
let container
|
||||
|
||||
if (thisObject.editMode === true) {
|
||||
if (point.x === VERY_LARGE_NUMBER) {
|
||||
/* The the mouse leaves the canvas, and event of mouse over with a ridiculous coordinate is triggered so that
|
||||
anyone can react. In our case, this object has an html input element that is not part of the canvas, so the event is
|
||||
triggered. We compensate recognizing this coordinate and returning our container. */
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (thisObject.payload.title === undefined) { return }
|
||||
|
||||
/* It is possible to override the default title by setting the APP SCHEMA property 'title' */
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type)
|
||||
if (nodeDefinition.title !== undefined) {
|
||||
thisObject.payload.title = ''
|
||||
thisObject.payload.node.name = ''
|
||||
let separator = ''
|
||||
for (let i = 0; i < nodeDefinition.title.length; i++) {
|
||||
let titleReference = nodeDefinition.title[i]
|
||||
switch (titleReference) {
|
||||
case 'Use Reference Parent': {
|
||||
let nodeToUse = thisObject.payload.node.payload.referenceParent
|
||||
if (nodeToUse !== undefined && nodeToUse.payload !== undefined) {
|
||||
thisObject.payload.title = thisObject.payload.title + separator + nodeToUse.payload.title
|
||||
thisObject.payload.node.name = thisObject.payload.node.name + separator + nodeToUse.payload.node.name
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Use Reference Grandparent': {
|
||||
let nodeToUse = thisObject.payload.node.payload.referenceParent
|
||||
if (nodeToUse !== undefined && nodeToUse.payload !== undefined) {
|
||||
nodeToUse = thisObject.payload.node.payload.referenceParent.payload.referenceParent
|
||||
if (nodeToUse !== undefined) {
|
||||
thisObject.payload.title = thisObject.payload.title + separator + nodeToUse.payload.title
|
||||
thisObject.payload.node.name = thisObject.payload.node.name + separator + nodeToUse.payload.node.name
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Use Reference Parent Parent': {
|
||||
let nodeToUse = thisObject.payload.node.payload.referenceParent
|
||||
if (nodeToUse !== undefined && nodeToUse.payload !== undefined) {
|
||||
nodeToUse = thisObject.payload.node.payload.referenceParent.payload.parentNode
|
||||
if (nodeToUse !== undefined && nodeToUse.payload !== undefined) {
|
||||
thisObject.payload.title = thisObject.payload.title + separator + nodeToUse.payload.title
|
||||
thisObject.payload.node.name = thisObject.payload.node.name + separator + nodeToUse.payload.node.name
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
case 'Use Reference Parent Parent Parent': {
|
||||
let nodeToUse = thisObject.payload.node.payload.referenceParent
|
||||
if (nodeToUse !== undefined && nodeToUse.payload !== undefined) {
|
||||
nodeToUse = thisObject.payload.node.payload.referenceParent.payload.parentNode
|
||||
if (nodeToUse !== undefined && nodeToUse.payload !== undefined) {
|
||||
nodeToUse = thisObject.payload.node.payload.referenceParent.payload.parentNode.payload.parentNode
|
||||
if (nodeToUse !== undefined && nodeToUse.payload !== undefined) {
|
||||
thisObject.payload.title = thisObject.payload.title + separator + nodeToUse.payload.title
|
||||
thisObject.payload.node.name = thisObject.payload.node.name + separator + nodeToUse.payload.node.name
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
separator = ' '
|
||||
}
|
||||
}
|
||||
|
||||
let title = trimTitle(thisObject.payload.title)
|
||||
|
||||
/* Here we set the dimensions and position of this object */
|
||||
const FRAME_HEIGHT = 25
|
||||
const Y_OFFSET = -20
|
||||
const FRAME_WIDTH = (title.length + 2) / 2 * thisObject.payload.floatingObject.currentFontSize * FONT_ASPECT_RATIO * 1.2 * 2
|
||||
thisObject.container.frame.position.x = 0 - FRAME_WIDTH / 2
|
||||
thisObject.container.frame.position.y = 0 - thisObject.container.frame.radius * 1 / 2 - thisObject.payload.floatingObject.currentFontSize * FONT_ASPECT_RATIO + Y_OFFSET - FRAME_HEIGHT
|
||||
|
||||
thisObject.container.frame.width = FRAME_WIDTH
|
||||
thisObject.container.frame.height = FRAME_HEIGHT
|
||||
|
||||
if (thisObject.editMode === true) {
|
||||
let inputPosition = {
|
||||
x: 0,
|
||||
y: 0 + CURRENT_TOP_MARGIN + Y_OFFSET
|
||||
}
|
||||
|
||||
inputPosition = thisObject.container.frame.frameThisPoint(inputPosition)
|
||||
|
||||
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; '
|
||||
}
|
||||
}
|
||||
|
||||
function onFocus () {
|
||||
thisObject.isOnFocus = true
|
||||
}
|
||||
|
||||
function onNotFocus () {
|
||||
thisObject.isOnFocus = false
|
||||
exitEditMode()
|
||||
}
|
||||
|
||||
function onMouseClick (event) {
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(thisObject.payload.node.type)
|
||||
if (nodeDefinition.title !== undefined) { return }
|
||||
|
||||
let checkPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
checkPoint = thisObject.container.frame.frameThisPoint(checkPoint)
|
||||
|
||||
if (thisObject.isVisibleFunction(checkPoint) === true) {
|
||||
enterEditMode()
|
||||
}
|
||||
}
|
||||
|
||||
function exitEditMode () {
|
||||
if (thisObject.editMode === true) {
|
||||
thisObject.editMode = false
|
||||
let input = document.getElementById('input')
|
||||
input.style.display = 'none'
|
||||
if (input.value.length > 2) {
|
||||
thisObject.payload.title = input.value
|
||||
thisObject.payload.node.name = input.value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function enterEditMode () {
|
||||
const WIDTH = thisObject.container.frame.width
|
||||
const HEIGHT = thisObject.container.frame.height
|
||||
let fontSize = thisObject.payload.floatingObject.currentFontSize
|
||||
|
||||
thisObject.editMode = true
|
||||
let input = document.getElementById('input')
|
||||
input.value = thisObject.payload.title
|
||||
|
||||
let backgroundColor = '0, 0, 0'
|
||||
if (thisObject.payload.uiObject.codeEditor !== undefined) {
|
||||
if (thisObject.payload.uiObject.codeEditor.visible === true) {
|
||||
backgroundColor = '204, 88, 53'
|
||||
}
|
||||
}
|
||||
|
||||
input.style = 'resize: none; border: none; outline: none; box-shadow: none; overflow:hidden; font-family: Saira Condensed; font-size: ' + fontSize + 'px; background-color: rgb(' + backgroundColor + ');color:rgb(255, 255, 255); width: ' + WIDTH + 'px; height: ' + HEIGHT + 'px'
|
||||
input.style.display = 'block'
|
||||
input.focus()
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.isOnFocus === true || thisObject.allwaysVisible === true) {
|
||||
if (thisObject.payload.uiObject.codeEditor !== undefined) {
|
||||
if (thisObject.payload.uiObject.codeEditor.visible !== true) {
|
||||
drawTitleBackground()
|
||||
}
|
||||
} else {
|
||||
drawTitleBackground()
|
||||
}
|
||||
|
||||
drawText()
|
||||
}
|
||||
}
|
||||
|
||||
function drawTitleBackground () {
|
||||
if (thisObject.editMode !== true) {
|
||||
let params = {
|
||||
cornerRadius: 3,
|
||||
lineWidth: 0.1,
|
||||
container: thisObject.container,
|
||||
borderColor: UI_COLOR.BLACK,
|
||||
backgroundColor: UI_COLOR.BLACK,
|
||||
castShadow: false,
|
||||
opacity: 0.25
|
||||
}
|
||||
|
||||
roundedCornersBackground(params)
|
||||
}
|
||||
}
|
||||
|
||||
function drawText () {
|
||||
if (thisObject.editMode !== true) {
|
||||
let radius = thisObject.container.frame.radius
|
||||
let labelPoint
|
||||
let fontSize = thisObject.payload.floatingObject.currentFontSize
|
||||
let label
|
||||
|
||||
if (radius > 6 && (thisObject.isOnFocus === true || thisObject.allwaysVisible === true)) {
|
||||
browserCanvasContext.strokeStyle = thisObject.payload.floatingObject.labelStrokeStyle
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
label = thisObject.payload.title
|
||||
|
||||
if (label !== undefined) {
|
||||
label = trimTitle(label)
|
||||
|
||||
labelPoint = {
|
||||
x: 5,
|
||||
y: thisObject.container.frame.height * 0.8
|
||||
}
|
||||
|
||||
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint)
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.fillStyle = thisObject.payload.floatingObject.labelStrokeStyle
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function trimTitle (title) {
|
||||
if (title === undefined) { return }
|
||||
const MAX_LABEL_LENGTH = 50
|
||||
if (title.length > MAX_LABEL_LENGTH) {
|
||||
title = title.substring(0, MAX_LABEL_LENGTH) + '...'
|
||||
}
|
||||
return title
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,511 @@
|
||||
|
||||
function newFrame () {
|
||||
const PANEL_CORNERS_RADIUS = 5
|
||||
const TITLE_BAR_HEIGHT = 15 // this must be grater than radius
|
||||
|
||||
let thisObject = {
|
||||
type: 'Rectangle',
|
||||
containerName: '', // This is for debugging purposes only.
|
||||
parentFrame: undefined, // Here we store the parent cointainer zoom object.
|
||||
radius: 0,
|
||||
offset: undefined,
|
||||
position: undefined,
|
||||
container: undefined,
|
||||
width: browserCanvas.width,
|
||||
height: browserCanvas.height,
|
||||
getBodyHeight: getBodyHeight,
|
||||
draw: draw,
|
||||
fitIntoFrame: fitIntoFrame,
|
||||
frameThisPoint: frameThisPoint, // This function changes the actual frame coordinate system to the screen coordinate system.
|
||||
unframeThisPoint: unframeThisPoint,
|
||||
isThisPointHere: isThisPointHere, // This function return true is the point received as parameter lives within this frame.
|
||||
isThisScreenPointHere: isThisScreenPointHere,
|
||||
isInViewPort: isInViewPort,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.parentFrame = undefined
|
||||
thisObject.position = undefined
|
||||
thisObject.offset = undefined
|
||||
}
|
||||
|
||||
function initialize (pType) {
|
||||
if (pType !== undefined) { thisObject.type = pType }
|
||||
|
||||
thisObject.position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
thisObject.offset = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
|
||||
function getBodyHeight () {
|
||||
return thisObject.height - TITLE_BAR_HEIGHT
|
||||
}
|
||||
|
||||
function isInViewPort () {
|
||||
/* This function is usefull to know if the object who has this frame is currently appearing at least in part at the canvas.chartingSpace.viewport */
|
||||
|
||||
point1 = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: thisObject.width,
|
||||
y: thisObject.height
|
||||
}
|
||||
|
||||
/* Now the transformations. */
|
||||
|
||||
point1 = transformThisPoint(point1, thisObject.container)
|
||||
point3 = transformThisPoint(point3, thisObject.container)
|
||||
|
||||
if (point1.x < canvas.chartingSpace.viewport.visibleArea.topRight.x && point1.y < canvas.chartingSpace.viewport.visibleArea.bottomRight.y && point3.x > canvas.chartingSpace.viewport.visibleArea.bottomLeft.x && point3.y > canvas.chartingSpace.viewport.visibleArea.topLeft.y) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function frameThisPoint (point) {
|
||||
/* We need not to modify the point received, so me make a copy of it. */
|
||||
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
let parentPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let returnPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
if (thisObject.parentFrame !== undefined) {
|
||||
parentPoint = thisObject.parentFrame.frameThisPoint(checkPoint)
|
||||
|
||||
returnPoint.x = parentPoint.x + thisObject.position.x + thisObject.offset.x
|
||||
returnPoint.y = parentPoint.y + thisObject.position.y + thisObject.offset.y
|
||||
} else {
|
||||
returnPoint.x = checkPoint.x + thisObject.position.x + thisObject.offset.x
|
||||
returnPoint.y = checkPoint.y + thisObject.position.y + thisObject.offset.y
|
||||
}
|
||||
|
||||
return returnPoint
|
||||
}
|
||||
|
||||
function unframeThisPoint (point) {
|
||||
/* We need not to modify the point received, so me make a copy of it. */
|
||||
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
let parentPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let returnPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
if (thisObject.parentFrame !== undefined) {
|
||||
parentPoint = thisObject.parentFrame.unframeThisPoint(checkPoint)
|
||||
|
||||
returnPoint.x = parentPoint.x - thisObject.position.x
|
||||
returnPoint.y = parentPoint.y - thisObject.position.y
|
||||
} else {
|
||||
returnPoint.x = checkPoint.x - thisObject.position.x
|
||||
returnPoint.y = checkPoint.y - thisObject.position.y
|
||||
}
|
||||
|
||||
return returnPoint
|
||||
}
|
||||
|
||||
function fitIntoFrame (point) {
|
||||
/* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */
|
||||
|
||||
if (point.x > thisObject.width) {
|
||||
point.x = thisObject.width
|
||||
}
|
||||
|
||||
if (point.x < 0) {
|
||||
point.x = 0
|
||||
}
|
||||
|
||||
if (point.y > thisObject.height) {
|
||||
point.y = thisObject.height
|
||||
}
|
||||
|
||||
if (point.y < 0) {
|
||||
point.y = 0
|
||||
}
|
||||
|
||||
return point
|
||||
}
|
||||
|
||||
function isThisPointHere (point, outsideViewPort, dontTransform, padding) {
|
||||
// The second parameter is usefull when you want to check a point that you already know is outside the viewport.
|
||||
// The padding is a distance to the borders of the container. Can be either negative (outside, to increase the cointainer size) or positive (inside to decrease the cointainer size)
|
||||
|
||||
if (padding === undefined) { padding = 0 }
|
||||
|
||||
/* We need not to modify the point received, so me make a copy of it. */
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
/* The point received is on the screen coordinates system, which already has zoom and displacement applied. We need to remove the zoom and displacement
|
||||
in order to have the point on the containers coordinate system and be able to compare it with its dimmensions. */
|
||||
if (dontTransform === false || dontTransform === undefined) {
|
||||
if (outsideViewPort === true) {
|
||||
checkPoint = thisObject.container.frame.unframeThisPoint(checkPoint)
|
||||
} else {
|
||||
checkPoint = unTransformThisPoint(checkPoint, thisObject.container)
|
||||
}
|
||||
}
|
||||
|
||||
/* Now we check if the resulting point is whin the current Frame. */
|
||||
|
||||
if (thisObject.type === 'Circle') {
|
||||
let distance = Math.sqrt(Math.pow(thisObject.position.x - point.x, 2) + Math.pow(thisObject.position.y - point.y, 2))
|
||||
|
||||
if (distance < thisObject.radius) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (thisObject.type === 'Rectangle') {
|
||||
if (checkPoint.x < 0 + padding || checkPoint.y < 0 + padding || checkPoint.x > thisObject.width - padding || checkPoint.y > thisObject.height - padding) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isThisScreenPointHere (point) {
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
let thisPoint = {
|
||||
x: thisObject.position.x,
|
||||
y: thisObject.position.y
|
||||
}
|
||||
|
||||
if (thisObject.parentFrame !== undefined) {
|
||||
thisPoint = thisObject.parentFrame.frameThisPoint(thisPoint)
|
||||
}
|
||||
|
||||
/* Now we check if the resulting point is whin the current Frame. */
|
||||
|
||||
if (thisObject.type === 'Circle') {
|
||||
let distance = Math.sqrt(Math.pow(thisPoint.x - checkPoint.x, 2) + Math.pow(thisPoint.y - checkPoint.y, 2))
|
||||
|
||||
if (distance < thisObject.radius) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
if (thisObject.type === 'Rectangle') {
|
||||
if (checkPoint.x < 0 || checkPoint.y < 0 || checkPoint.x > thisObject.width || checkPoint.y > thisObject.height) {
|
||||
return false
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function draw (drawGrid, drawBorders, drawBackground, fitFunction, style) {
|
||||
if (drawBorders === true) {
|
||||
borders(fitFunction, style)
|
||||
}
|
||||
|
||||
if (drawGrid === true) {
|
||||
grid(fitFunction)
|
||||
}
|
||||
|
||||
if (drawBackground === true) {
|
||||
background(fitFunction)
|
||||
}
|
||||
}
|
||||
|
||||
function background (fitFunction) {
|
||||
let params = {
|
||||
cornerRadius: 5,
|
||||
lineWidth: 0.1,
|
||||
opacity: 0.75,
|
||||
container: thisObject.container,
|
||||
borderColor: UI_COLOR.DARK,
|
||||
backgroundColor: UI_COLOR.WHITE,
|
||||
fitFunction: fitFunction
|
||||
}
|
||||
|
||||
roundedCornersBackground(params)
|
||||
|
||||
titleBarPoint1 = {
|
||||
x: 0,
|
||||
y: TITLE_BAR_HEIGHT
|
||||
}
|
||||
|
||||
titleBarPoint2 = {
|
||||
x: thisObject.width,
|
||||
y: TITLE_BAR_HEIGHT
|
||||
}
|
||||
|
||||
/* Now the transformations. */
|
||||
|
||||
titleBarPoint1 = frameThisPoint(titleBarPoint1)
|
||||
titleBarPoint2 = frameThisPoint(titleBarPoint2)
|
||||
|
||||
if (fitFunction !== undefined) {
|
||||
titleBarPoint1 = fitFunction(titleBarPoint1)
|
||||
titleBarPoint2 = fitFunction(titleBarPoint2)
|
||||
}
|
||||
|
||||
/* We paint the title bar now */
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', 1)'
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
browserCanvasContext.moveTo(titleBarPoint1.x, titleBarPoint1.y)
|
||||
browserCanvasContext.lineTo(borderPoint1.x - PANEL_CORNERS_RADIUS, borderPoint1.y)
|
||||
browserCanvasContext.arc(borderPoint1.x, borderPoint1.y, PANEL_CORNERS_RADIUS, 1.0 * Math.PI, 1.5 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint2.x, borderPoint2.y - PANEL_CORNERS_RADIUS)
|
||||
browserCanvasContext.arc(borderPoint2.x, borderPoint2.y, PANEL_CORNERS_RADIUS, 1.5 * Math.PI, 2.0 * Math.PI)
|
||||
browserCanvasContext.lineTo(titleBarPoint2.x, titleBarPoint2.y)
|
||||
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.lineWidth = 0.1
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.MANGANESE_PURPLE + ', 0.75)'
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
/* print the title */
|
||||
|
||||
let labelPoint
|
||||
let fontSize = 10
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
|
||||
let label = thisObject.containerName
|
||||
|
||||
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO
|
||||
let yOffset = (TITLE_BAR_HEIGHT - fontSize) / 2 + 2
|
||||
|
||||
labelPoint = {
|
||||
x: thisObject.width / 2 - xOffset,
|
||||
y: TITLE_BAR_HEIGHT - yOffset
|
||||
}
|
||||
|
||||
labelPoint = thisObject.frameThisPoint(labelPoint)
|
||||
|
||||
if (fitFunction !== undefined) {
|
||||
labelPoint = fitFunction(labelPoint)
|
||||
}
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
|
||||
function borders (fitFunction, style) {
|
||||
/* Lest get the important points of the drawing so as to apply the needed transformations. */
|
||||
|
||||
let point1
|
||||
let point2
|
||||
let point3
|
||||
let point4
|
||||
|
||||
point1 = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: thisObject.width,
|
||||
y: 0
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: thisObject.width,
|
||||
y: thisObject.height
|
||||
}
|
||||
|
||||
point4 = {
|
||||
x: 0,
|
||||
y: thisObject.height
|
||||
}
|
||||
|
||||
/* Now the transformations. */
|
||||
point1 = transformThisPoint(point1, thisObject.container)
|
||||
point2 = transformThisPoint(point2, thisObject.container)
|
||||
point3 = transformThisPoint(point3, thisObject.container)
|
||||
point4 = transformThisPoint(point4, thisObject.container)
|
||||
|
||||
if (fitFunction !== undefined) {
|
||||
point1 = fitFunction(point1)
|
||||
point2 = fitFunction(point2)
|
||||
point3 = fitFunction(point3)
|
||||
point4 = fitFunction(point4)
|
||||
}
|
||||
|
||||
/* Lets start the drawing. */
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
browserCanvasContext.lineTo(point3.x, point3.y)
|
||||
browserCanvasContext.lineTo(point4.x, point4.y)
|
||||
browserCanvasContext.lineTo(point1.x, point1.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + style.color + ', ' + style.opacity + ')'
|
||||
browserCanvasContext.lineWidth = style.lineWidth
|
||||
browserCanvasContext.setLineDash(style.lineDash)
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
browserCanvasContext.closePath()
|
||||
}
|
||||
|
||||
function grid (fitFunction, smallLines) {
|
||||
if (smallLines === true) {
|
||||
/* Small Lines */
|
||||
|
||||
let step = thisObject.width / 100
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
for (let i = 0; i < thisObject.width; i = i + step) {
|
||||
for (let j = 0; j < thisObject.height; j = j + step) {
|
||||
let point1 = {
|
||||
x: 0,
|
||||
y: j
|
||||
}
|
||||
|
||||
let point2 = {
|
||||
x: thisObject.width,
|
||||
y: j
|
||||
}
|
||||
|
||||
point1 = transformThisPoint(point1, thisObject.container)
|
||||
point2 = transformThisPoint(point2, thisObject.container)
|
||||
|
||||
if (fitFunction !== undefined) {
|
||||
point1 = fitFunction(point1)
|
||||
point2 = fitFunction(point2)
|
||||
}
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
}
|
||||
|
||||
let point3 = {
|
||||
x: thisObject.width,
|
||||
y: thisObject.height
|
||||
}
|
||||
|
||||
let point4 = {
|
||||
x: 0,
|
||||
y: thisObject.height
|
||||
}
|
||||
|
||||
point3 = transformThisPoint(point3, thisObject.container)
|
||||
point4 = transformThisPoint(point4, thisObject.container)
|
||||
|
||||
if (fitFunction !== undefined) {
|
||||
point3 = fitFunction(point3)
|
||||
point4 = fitFunction(point4)
|
||||
}
|
||||
|
||||
browserCanvasContext.moveTo(point3.x, point3.y)
|
||||
browserCanvasContext.lineTo(point4.x, point4.y)
|
||||
}
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.1)'
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
|
||||
/* Main Lines */
|
||||
|
||||
let step = thisObject.width / 20
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
for (let i = 0; i < thisObject.width; i = i + step) {
|
||||
for (let j = 0; j < thisObject.height; j = j + step) {
|
||||
let point1 = {
|
||||
x: 0,
|
||||
y: j
|
||||
}
|
||||
|
||||
let point2 = {
|
||||
x: thisObject.width,
|
||||
y: j
|
||||
}
|
||||
|
||||
point1 = transformThisPoint(point1, thisObject.container)
|
||||
point2 = transformThisPoint(point2, thisObject.container)
|
||||
|
||||
if (fitFunction !== undefined) {
|
||||
point1 = fitFunction(point1)
|
||||
point2 = fitFunction(point2)
|
||||
}
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
}
|
||||
|
||||
let point3 = {
|
||||
x: i,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let point4 = {
|
||||
x: i,
|
||||
y: thisObject.height
|
||||
}
|
||||
|
||||
point3 = transformThisPoint(point3, thisObject.container)
|
||||
point4 = transformThisPoint(point4, thisObject.container)
|
||||
|
||||
if (fitFunction !== undefined) {
|
||||
point3 = fitFunction(point3)
|
||||
point4 = fitFunction(point4)
|
||||
}
|
||||
|
||||
browserCanvasContext.moveTo(point3.x, point3.y)
|
||||
browserCanvasContext.lineTo(point4.x, point4.y)
|
||||
}
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.strokeStyle = 'rgba(100, 100, 100, 0.2)'
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,221 @@
|
||||
|
||||
/* User Interface Colors */
|
||||
|
||||
const CANVAS_APP_NAME = 'Canvas App'
|
||||
|
||||
const SUPPORTED_EXCHANGES = ["Poloniex"];
|
||||
const SUPPORTED_MARKETS = [
|
||||
{
|
||||
baseAsset: "USDT",
|
||||
quotedAsset: "BTC",
|
||||
}
|
||||
];
|
||||
const DEFAULT_EXCHANGE = "Poloniex";
|
||||
const DEFAULT_MARKET = {
|
||||
baseAsset: "USDT",
|
||||
quotedAsset: "BTC",
|
||||
};
|
||||
|
||||
UI_COLOR = {
|
||||
DARK: "48, 48, 54",
|
||||
LIGHT: "234, 226, 222",
|
||||
GREY: "150, 150, 150",
|
||||
LIGHT_GREY: "247, 247, 247",
|
||||
WHITE: "255, 255, 255",
|
||||
BLACK: "0, 0, 0",
|
||||
GOLDEN_ORANGE: "240, 162, 2",
|
||||
RUSTED_RED: "204, 88, 53",
|
||||
GREEN: "188, 214, 67",
|
||||
RED: "223, 70, 60",
|
||||
PATINATED_TURQUOISE: "27,153,139",
|
||||
TITANIUM_YELLOW: "244,228,9",
|
||||
MANGANESE_PURPLE: "91,80,122",
|
||||
TURQUOISE: "74,207,217",
|
||||
DARK_TURQUOISE: "2,149,170"
|
||||
};
|
||||
|
||||
/* User Interface Fonts */
|
||||
|
||||
UI_FONT = {
|
||||
PRIMARY: "Saira Condensed",
|
||||
SECONDARY: "Source Code Pro"
|
||||
};
|
||||
|
||||
const DEBUG = {}
|
||||
const ZOOM_OUT_THRESHOLD_FOR_HIDDING_PANELS = 5 // This regulates some behavious changes that happens when the user zoom out below this level.
|
||||
const ZOOM_OUT_THRESHOLD_FOR_CHANGING_TIME_FRAME = 5 // This help regulate when to change the Time Frame base on the level of zoom out.
|
||||
const ZOOM_OUT_THRESHOLD_FOR_PACKING_OBJECTS_AT_THE_BOTTOM_OR_TOP_OF_VIEWPORT = 5
|
||||
|
||||
let FONT_ASPECT_RATIO = 0.32;
|
||||
|
||||
|
||||
const GET_CONTAINER_PURPOSE = {
|
||||
MOUSE_OVER: 1,
|
||||
MOUSE_WHEEL: 2,
|
||||
MOUSE_CLICK: 3,
|
||||
DRAGGING: 4
|
||||
}
|
||||
|
||||
const ANGLE_TO_PARENT = {
|
||||
NOT_FIXED: 0,
|
||||
RANGE_360: 1,
|
||||
RANGE_180: 2,
|
||||
RANGE_90: 3,
|
||||
RANGE_45: 4
|
||||
}
|
||||
|
||||
const DISTANCE_TO_PARENT = {
|
||||
NOT_FIXED: 0,
|
||||
PARENT_025X: 1,
|
||||
PARENT_050X: 2,
|
||||
PARENT_100X: 3,
|
||||
PARENT_150X: 4,
|
||||
PARENT_200X: 5
|
||||
}
|
||||
|
||||
/* User Interface Panels */
|
||||
|
||||
UI_PANEL = {
|
||||
WIDTH: {
|
||||
SMALL: 100,
|
||||
NORMAL: 150,
|
||||
MEDIUM: 200,
|
||||
LARGE: 250,
|
||||
X_LARGE: 350
|
||||
},
|
||||
HEIGHT: {
|
||||
SMALL: 65,
|
||||
MEDIUM: 90,
|
||||
NORMAL: 300,
|
||||
LARGE: 450
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
let marketFilesPeriods =
|
||||
'[' +
|
||||
'[' + 24 * 60 * 60 * 1000 + ',' + '"24-hs"' + ']' + ',' +
|
||||
'[' + 12 * 60 * 60 * 1000 + ',' + '"12-hs"' + ']' + ',' +
|
||||
'[' + 8 * 60 * 60 * 1000 + ',' + '"08-hs"' + ']' + ',' +
|
||||
'[' + 6 * 60 * 60 * 1000 + ',' + '"06-hs"' + ']' + ',' +
|
||||
'[' + 4 * 60 * 60 * 1000 + ',' + '"04-hs"' + ']' + ',' +
|
||||
'[' + 3 * 60 * 60 * 1000 + ',' + '"03-hs"' + ']' + ',' +
|
||||
'[' + 2 * 60 * 60 * 1000 + ',' + '"02-hs"' + ']' + ',' +
|
||||
'[' + 1 * 60 * 60 * 1000 + ',' + '"01-hs"' + ']' + ']';
|
||||
|
||||
marketFilesPeriods = JSON.parse(marketFilesPeriods);
|
||||
|
||||
let dailyFilePeriods =
|
||||
'[' +
|
||||
'[' + 45 * 60 * 1000 + ',' + '"45-min"' + ']' + ',' +
|
||||
'[' + 40 * 60 * 1000 + ',' + '"40-min"' + ']' + ',' +
|
||||
'[' + 30 * 60 * 1000 + ',' + '"30-min"' + ']' + ',' +
|
||||
'[' + 20 * 60 * 1000 + ',' + '"20-min"' + ']' + ',' +
|
||||
'[' + 15 * 60 * 1000 + ',' + '"15-min"' + ']' + ',' +
|
||||
'[' + 10 * 60 * 1000 + ',' + '"10-min"' + ']' + ',' +
|
||||
'[' + 05 * 60 * 1000 + ',' + '"05-min"' + ']' + ',' +
|
||||
'[' + 04 * 60 * 1000 + ',' + '"04-min"' + ']' + ',' +
|
||||
'[' + 03 * 60 * 1000 + ',' + '"03-min"' + ']' + ',' +
|
||||
'[' + 02 * 60 * 1000 + ',' + '"02-min"' + ']' + ',' +
|
||||
'[' + 01 * 60 * 1000 + ',' + '"01-min"' + ']' + ']';
|
||||
|
||||
dailyFilePeriods = JSON.parse(dailyFilePeriods);
|
||||
|
||||
SHOW_ANIMATION_PERFORMACE = false;
|
||||
|
||||
let testUser = window.localStorage.getItem("test_user")
|
||||
let LOGGED_IN_USER_LOCAL_STORAGE_KEY
|
||||
let LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY
|
||||
|
||||
if (testUser !== null) {
|
||||
LOGGED_IN_USER_LOCAL_STORAGE_KEY = "test_user"
|
||||
LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY = "test_access_token"
|
||||
} else {
|
||||
LOGGED_IN_USER_LOCAL_STORAGE_KEY = "user"
|
||||
LOGGED_IN_ACCESS_TOKEN_LOCAL_STORAGE_KEY = "access_token"
|
||||
}
|
||||
|
||||
const DEFAULT_CONFIG_TEXT = '// Write your config here'
|
||||
const DEFAULT_FORMULA_TEXT = '// Write your formula here'
|
||||
const DEFAULT_CODE_TEXT = '// Write your code here'
|
||||
|
||||
const MAX_DEFAULT_RATE_SCALE_VALUE = 35000; // This is needed to know the scale of the market time line.
|
||||
|
||||
const WIDHTER_VOLUME_BAR_BASE_FACTOR = 2.5;
|
||||
const LESS_WIDHTER_VOLUME_BAR_TOP_FACTOR = 1 / 4;
|
||||
|
||||
const ONE_DAY_IN_MILISECONDS = 24 * 60 * 60 * 1000;
|
||||
const _12_HOURS_IN_MILISECONDS = 12 * 60 * 60 * 1000;
|
||||
const _8_HOURS_IN_MILISECONDS = 8 * 60 * 60 * 1000;
|
||||
const _6_HOURS_IN_MILISECONDS = 6 * 60 * 60 * 1000;
|
||||
const _4_HOURS_IN_MILISECONDS = 4 * 60 * 60 * 1000;
|
||||
const _3_HOURS_IN_MILISECONDS = 3 * 60 * 60 * 1000;
|
||||
const _2_HOURS_IN_MILISECONDS = 2 * 60 * 60 * 1000;
|
||||
const _1_HOUR_IN_MILISECONDS = 1 * 60 * 60 * 1000;
|
||||
|
||||
const _45_MINUTES_IN_MILISECONDS = 45 * 60 * 1000;
|
||||
const _40_MINUTES_IN_MILISECONDS = 40 * 60 * 1000;
|
||||
const _30_MINUTES_IN_MILISECONDS = 30 * 60 * 1000;
|
||||
const _20_MINUTES_IN_MILISECONDS = 20 * 60 * 1000;
|
||||
const _15_MINUTES_IN_MILISECONDS = 15 * 60 * 1000;
|
||||
const _10_MINUTES_IN_MILISECONDS = 10 * 60 * 1000;
|
||||
const _5_MINUTES_IN_MILISECONDS = 5 * 60 * 1000;
|
||||
const _4_MINUTES_IN_MILISECONDS = 4 * 60 * 1000;
|
||||
const _3_MINUTES_IN_MILISECONDS = 3 * 60 * 1000;
|
||||
const _2_MINUTES_IN_MILISECONDS = 2 * 60 * 1000;
|
||||
const _1_MINUTE_IN_MILISECONDS = 1 * 60 * 1000;
|
||||
|
||||
let NEW_SESSION_INITIAL_DATE = new Date(); // This value will be overwritten at the canvas.chartingSpace.viewport.initialize if the user had a prevous session with this same browser.
|
||||
let INITIAL_ZOOM_LEVEL = -28.25 // This is the zoom level at the view port in which the APP starts.
|
||||
let INITIAL_TIME_PERIOD = ONE_DAY_IN_MILISECONDS // This value will be overwritten at the canvas.chartingSpace.viewport.initialize if the user had a prevous session with this same browser.
|
||||
let VERY_LARGE_NUMBER = 100000000000000
|
||||
|
||||
let CURRENT_TOP_MARGIN = window.canvasApp.topMargin
|
||||
let AT_FULL_SCREEN_MODE = false
|
||||
|
||||
let maxDate = new Date();
|
||||
maxDate.setMilliseconds(0);
|
||||
maxDate.setDate(maxDate.getDate() + 365 * 1); // We might have charts that projects data into the future.
|
||||
|
||||
const MIN_PLOTABLE_DATE = new Date(2015, 0, 1, 0, 0, 0);
|
||||
const MAX_PLOTABLE_DATE = maxDate;
|
||||
|
||||
const TOP_SPACE_HEIGHT = 40
|
||||
const COCKPIT_SPACE_HEIGHT = 30;
|
||||
const BREAKPOINT_HEIGHT = 15;
|
||||
const SIDE_PANEL_WIDTH = 450
|
||||
let COCKPIT_SPACE_POSITION = browserCanvas.height - COCKPIT_SPACE_HEIGHT
|
||||
|
||||
const LAYER_STATUS = {
|
||||
ON: 'on',
|
||||
LOADING: 'loading',
|
||||
OFF: 'off'
|
||||
};
|
||||
|
||||
let spawnPosition // this is used in several places.
|
||||
|
||||
/* Here we list the valid Time Periods: */
|
||||
|
||||
const PERIOD_24_HS = "24-hs";
|
||||
const PERIOD_12_HS = "12-hs";
|
||||
const PERIOD_06_HS = "06-hs";
|
||||
const PERIOD_03_HS = "03-hs";
|
||||
const PERIOD_01_HS = "01-hs";
|
||||
const PERIOD_30_MIN = "30-min";
|
||||
const PERIOD_10_MIN = "10-min";
|
||||
const PERIOD_05_MIN = "05-min";
|
||||
const PERIOD_01_MIN = "01-min";
|
||||
|
||||
/*
|
||||
We define here the size of the chartingSpace. It has to bee enough big in order to accomodate all the charts we expect to display in this space.
|
||||
*/
|
||||
|
||||
const TIME_MACHINE_WIDTH = 320;
|
||||
const TIME_MACHINE_HEIGHT = 120;
|
||||
|
||||
let mediaRecorder // to downloadText canvas animation as a mediaRecorder
|
||||
let areWeRecording = false
|
||||
|
||||
function toRadians(angle) {
|
||||
return angle * (Math.PI / 180);
|
||||
}
|
||||
@@ -0,0 +1,531 @@
|
||||
function newLayer () {
|
||||
const MODULE_NAME = 'Layer'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
status: LAYER_STATUS.OFF,
|
||||
fitFunction: undefined,
|
||||
payload: undefined,
|
||||
definition: undefined,
|
||||
panels: undefined,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
turnOff: turnOff,
|
||||
turnOn: turnOn,
|
||||
setDatetime: setDatetime,
|
||||
setTimeFrame: setTimeFrame,
|
||||
onMarketFileLoaded: onMarketFileLoaded,
|
||||
onDailyFileLoaded: onDailyFileLoaded,
|
||||
onSingleFileLoaded: onSingleFileLoaded,
|
||||
onFileSequenceLoaded: onFileSequenceLoaded,
|
||||
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let LOADING_FILL_STYLE = 'rgba(234, 143, 23, @Opacity)'
|
||||
let LOADED_FILL_STYLE = 'rgba(45, 232, 28, @Opacity)'
|
||||
let UNLOADED_FILL_STYLE = 'rgba(226, 226, 226, @Opacity)'
|
||||
|
||||
let LOADING_STROKE_STYLE = 'rgba(234, 143, 23, @Opacity)'
|
||||
let LOADED_STROKE_STYLE = 'rgba(45, 232, 28, @Opacity)'
|
||||
let UNLOADED_STROKE_STYLE = 'rgba(226, 226, 226, @Opacity)'
|
||||
|
||||
let marketFileProgressBar = {
|
||||
value: 0,
|
||||
animatedValue: 0,
|
||||
fillStyle: UNLOADED_FILL_STYLE,
|
||||
strokeStyle: UNLOADED_STROKE_STYLE,
|
||||
opacity: 0.00
|
||||
}
|
||||
|
||||
let dailyFileProgressBar = {
|
||||
value: 0,
|
||||
animatedValue: 0,
|
||||
fillStyle: UNLOADED_FILL_STYLE,
|
||||
strokeStyle: UNLOADED_STROKE_STYLE,
|
||||
opacity: 0.00
|
||||
}
|
||||
|
||||
let singleFileProgressBar = {
|
||||
value: 0,
|
||||
animatedValue: 0,
|
||||
fillStyle: UNLOADED_FILL_STYLE,
|
||||
strokeStyle: UNLOADED_STROKE_STYLE,
|
||||
opacity: 0.00
|
||||
}
|
||||
|
||||
let fileSequenceProgressBar = {
|
||||
value: 0,
|
||||
animatedValue: 0,
|
||||
fillStyle: UNLOADED_FILL_STYLE,
|
||||
strokeStyle: UNLOADED_STROKE_STYLE,
|
||||
opacity: 0.00
|
||||
}
|
||||
|
||||
let timeFrame = INITIAL_TIME_PERIOD
|
||||
let datetime = NEW_SESSION_INITIAL_DATE
|
||||
|
||||
/* Add an Event Handler */
|
||||
|
||||
thisObject.eventHandler = newEventHandler()
|
||||
let imagesLoaded = 0
|
||||
let onMouseClickEventSuscriptionId
|
||||
let panelsVisibleButton
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.finalize()
|
||||
thisObject.eventHandler.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.eventHandler = undefined
|
||||
thisObject.status = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.payload = undefined
|
||||
thisObject.definition = undefined
|
||||
thisObject.panels = undefined
|
||||
|
||||
timeFrame = undefined
|
||||
datetime = undefined
|
||||
|
||||
LOADING_FILL_STYLE = undefined
|
||||
LOADED_FILL_STYLE = undefined
|
||||
UNLOADED_FILL_STYLE = undefined
|
||||
|
||||
LOADING_STROKE_STYLE = undefined
|
||||
LOADED_STROKE_STYLE = undefined
|
||||
UNLOADED_STROKE_STYLE = undefined
|
||||
|
||||
marketFileProgressBar = undefined
|
||||
dailyFileProgressBar = undefined
|
||||
singleFileProgressBar = undefined
|
||||
fileSequenceProgressBar = undefined
|
||||
|
||||
panelsVisibleButton = undefined
|
||||
|
||||
thisObject.container.eventHandler.stopListening(onMouseClickEventSuscriptionId)
|
||||
}
|
||||
|
||||
function initialize (callBackFunction) {
|
||||
/* Create this objects continer */
|
||||
try {
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME + ' ' + thisObject.payload.node.id)
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.isClickeable = true
|
||||
|
||||
/* Lets set the basic dimensions of this thisObject. */
|
||||
let position = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
thisObject.container.frame.position = position
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL
|
||||
thisObject.container.frame.height = 70
|
||||
|
||||
let functionLibraryProtocolNode = newProtocolNode()
|
||||
let lightingPath =
|
||||
'->Layer->' +
|
||||
'Data Product->Single Market Data->' +
|
||||
'Session Independent Data->Session Based Data->' +
|
||||
'Session Reference->Backtesting Session->Paper Trading Session->Fordward Testing Session->Live Trading Session->' +
|
||||
'Market->Market Base Asset->Asset->' +
|
||||
'Market Quoted Asset->Asset->' +
|
||||
'Exchange Markets->Crypto Exchange->' +
|
||||
'Product Definition->' +
|
||||
'Sensor Bot->Indicator Bot->Trading Bot->' +
|
||||
'Data Mine->' +
|
||||
'Dataset Definition->' +
|
||||
'Record Definition->Record Property->Formula->' +
|
||||
'Data Building Procedure->Procedure Loop->Javascript Code->Procedure Initialization->Javascript Code->' +
|
||||
'Calculations Procedure->Procedure Loop->Javascript Code->Procedure Initialization->Javascript Code->' +
|
||||
'Plotter Module->Plotter->' +
|
||||
'Shapes->' +
|
||||
'Chart Points->Point->Point Formula->' +
|
||||
'Polygon->Polygon Body->Style->Style Condition->Style->' +
|
||||
'Polygon Border->Style->Style Condition->Style->' +
|
||||
'Polygon Vertex->Point->' +
|
||||
'Plotter Panel->Javascript Code->Panel Data->Data Formula->'
|
||||
thisObject.definition = functionLibraryProtocolNode.getProtocolNode(thisObject.payload.node, false, true, true, false, false, lightingPath)
|
||||
|
||||
/* Here we validate that we have all the needed information */
|
||||
if (thisObject.definition.referenceParent === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.parentNode === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.parentNode.referenceParent === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.parentNode.referenceParent.parentNode === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.parentNode.referenceParent.parentNode.parentNode === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.parentNode.referenceParent.baseAsset === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.parentNode.referenceParent.baseAsset.referenceParent === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.parentNode.referenceParent.quotedAsset === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.parentNode.referenceParent.quotedAsset.referenceParent === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.referenceParent === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.referenceParent.parentNode === undefined) { return }
|
||||
if (thisObject.definition.referenceParent.referenceParent.parentNode.parentNode === undefined) { return }
|
||||
|
||||
/* Lets listen to our own events to react when we have a Mouse Click */
|
||||
onMouseClickEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
|
||||
|
||||
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE)
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if (panelsVisibleButton !== undefined) {
|
||||
let container = panelsVisibleButton.getContainer(point)
|
||||
if (container !== undefined) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime (pDatetime) {
|
||||
/*
|
||||
When the datetime changes from one day to another, this forces cursors to potentially load more files, thus we reset this counter and
|
||||
get ready to receive events on files loaded.
|
||||
*/
|
||||
let currentDate = Math.trunc(datetime.valueOf() / ONE_DAY_IN_MILISECONDS)
|
||||
let newDate = Math.trunc(pDatetime.valueOf() / ONE_DAY_IN_MILISECONDS)
|
||||
|
||||
datetime = pDatetime
|
||||
|
||||
if (currentDate !== newDate) {
|
||||
if (timeFrame <= _1_HOUR_IN_MILISECONDS) {
|
||||
dailyFileProgressBar.animatedValue = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setTimeFrame (pTimeFrame) {
|
||||
/*
|
||||
When the time period below or equal to 1 hour changes, this forces cursors to potentially load more files, thus we reset this counter and
|
||||
get ready to receive events on files loaded.
|
||||
*/
|
||||
if (timeFrame !== pTimeFrame) {
|
||||
timeFrame = pTimeFrame
|
||||
|
||||
if (timeFrame <= _1_HOUR_IN_MILISECONDS) {
|
||||
dailyFileProgressBar.animatedValue = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onMarketFileLoaded (event) {
|
||||
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 }
|
||||
}
|
||||
|
||||
function onDailyFileLoaded (event) {
|
||||
dailyFileProgressBar.value = Math.trunc(event.currentValue * 100 / event.totalValue)
|
||||
dailyFileProgressBar.fillStyle = LOADING_FILL_STYLE
|
||||
dailyFileProgressBar.strokeStyle = LOADING_STROKE_STYLE
|
||||
|
||||
if (dailyFileProgressBar.value > 100) { dailyFileProgressBar.value = 100 }
|
||||
}
|
||||
|
||||
function onSingleFileLoaded (event) {
|
||||
singleFileProgressBar.value = Math.trunc(event.currentValue * 100 / event.totalValue)
|
||||
singleFileProgressBar.fillStyle = LOADING_FILL_STYLE
|
||||
singleFileProgressBar.strokeStyle = LOADING_STROKE_STYLE
|
||||
|
||||
if (singleFileProgressBar.value > 100) { singleFileProgressBar.value = 100 }
|
||||
}
|
||||
|
||||
function onFileSequenceLoaded (event) {
|
||||
fileSequenceProgressBar.value = Math.trunc(event.currentValue * 100 / event.totalValue)
|
||||
fileSequenceProgressBar.fillStyle = LOADING_FILL_STYLE
|
||||
fileSequenceProgressBar.strokeStyle = LOADING_STROKE_STYLE
|
||||
|
||||
if (fileSequenceProgressBar.value > 100) { fileSequenceProgressBar.value = 100 }
|
||||
}
|
||||
|
||||
function turnOff () {
|
||||
if (thisObject.status !== LAYER_STATUS.OFF) {
|
||||
resetProgressBars()
|
||||
changeStatusTo(LAYER_STATUS.OFF)
|
||||
}
|
||||
}
|
||||
|
||||
function turnOn () {
|
||||
if (thisObject.status === LAYER_STATUS.OFF) {
|
||||
resetProgressBars()
|
||||
changeStatusTo(LAYER_STATUS.LOADING)
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseClick (event) {
|
||||
switch (thisObject.status) {
|
||||
case LAYER_STATUS.ON:
|
||||
resetProgressBars()
|
||||
changeStatusTo(LAYER_STATUS.OFF)
|
||||
break
|
||||
case LAYER_STATUS.OFF:
|
||||
resetProgressBars()
|
||||
changeStatusTo(LAYER_STATUS.LOADING)
|
||||
break
|
||||
case LAYER_STATUS.LOADING:
|
||||
changeStatusTo(LAYER_STATUS.OFF)
|
||||
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 physics () {
|
||||
statusPhysics()
|
||||
childrenPhysics()
|
||||
}
|
||||
|
||||
function childrenPhysics () {
|
||||
/* Panels Visible Button Setup */
|
||||
if (thisObject.status === LAYER_STATUS.ON) {
|
||||
if (panelsVisibleButton === undefined) {
|
||||
if (thisObject.panels !== undefined) {
|
||||
if (thisObject.panels.length > 0) {
|
||||
panelsVisibleButton = newPanelsVisibleButton()
|
||||
|
||||
let storedValue = loadPropertyFromNodeConfig(thisObject.payload, 'showPanels')
|
||||
if (storedValue !== undefined) {
|
||||
if (storedValue === true || storedValue === false) {
|
||||
panelsVisibleButton.showPanels = storedValue
|
||||
}
|
||||
}
|
||||
|
||||
panelsVisibleButton.initialize(thisObject.panels)
|
||||
panelsVisibleButton.container.connectToParent(thisObject.container)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
savePropertyAtNodeConfig(thisObject.payload, 'showPanels', panelsVisibleButton.showPanels)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function statusPhysics () {
|
||||
/* We retrieve the stored status at the config. */
|
||||
try {
|
||||
let storedValue = loadPropertyFromNodeConfig(thisObject.payload, 'status')
|
||||
|
||||
if (storedValue !== undefined) {
|
||||
if (storedValue !== thisObject.status) {
|
||||
if (storedValue === LAYER_STATUS.ON) {
|
||||
resetProgressBars()
|
||||
changeStatusTo(LAYER_STATUS.LOADING)
|
||||
}
|
||||
if (storedValue === LAYER_STATUS.OFF) {
|
||||
resetProgressBars()
|
||||
changeStatusTo(LAYER_STATUS.OFF)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
resetProgressBars()
|
||||
changeStatusTo(LAYER_STATUS.OFF)
|
||||
}
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
|
||||
/* Check when the loading finishes */
|
||||
if (
|
||||
thisObject.status === LAYER_STATUS.LOADING &&
|
||||
marketFileProgressBar.value === 100 &&
|
||||
dailyFileProgressBar.value === 100
|
||||
) {
|
||||
changeStatusTo(LAYER_STATUS.ON)
|
||||
}
|
||||
}
|
||||
|
||||
function changeStatusTo (newStatus) {
|
||||
if (thisObject.status !== newStatus) {
|
||||
thisObject.status = newStatus
|
||||
|
||||
if (thisObject.status === LAYER_STATUS.OFF) {
|
||||
if (panelsVisibleButton !== undefined) {
|
||||
panelsVisibleButton.finalize()
|
||||
panelsVisibleButton = undefined
|
||||
}
|
||||
}
|
||||
|
||||
savePropertyAtNodeConfig(thisObject.payload, 'status', thisObject.status)
|
||||
let eventData = thisObject
|
||||
thisObject.container.eventHandler.raiseEvent('Status Changed', eventData)
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
drawLayer()
|
||||
if (panelsVisibleButton !== undefined) {
|
||||
panelsVisibleButton.draw()
|
||||
}
|
||||
}
|
||||
|
||||
function drawLayer () {
|
||||
drawLayerDisplay()
|
||||
}
|
||||
|
||||
function drawProgressBar (progressBar, lineWidth, offsetY) {
|
||||
const ANIMATED_INCREMENT = 5
|
||||
const OPACITY_INCREMENT = 0.01
|
||||
const OPACITY_MIN = 1
|
||||
|
||||
let point1
|
||||
let point2
|
||||
let horizontalMargin = 12
|
||||
|
||||
if (progressBar.value === 0) {
|
||||
return
|
||||
}
|
||||
|
||||
/* Animate */
|
||||
if (progressBar.animatedValue < progressBar.value) {
|
||||
progressBar.animatedValue = progressBar.animatedValue + ANIMATED_INCREMENT
|
||||
progressBar.opacity = progressBar.opacity + OPACITY_INCREMENT
|
||||
}
|
||||
|
||||
if (progressBar.animatedValue >= 100) {
|
||||
progressBar.animatedValue = 100
|
||||
|
||||
progressBar.opacity = progressBar.opacity - OPACITY_INCREMENT
|
||||
if (progressBar.opacity < OPACITY_MIN) { progressBar.opacity = OPACITY_MIN }
|
||||
|
||||
progressBar.strokeStyle = LOADED_STROKE_STYLE.replace('@Opacity', progressBar.opacity.toString())
|
||||
}
|
||||
|
||||
point1 = {
|
||||
x: 0 + horizontalMargin,
|
||||
y: thisObject.container.frame.height + offsetY
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: (thisObject.container.frame.width - horizontalMargin * 2) * progressBar.animatedValue / 100 + horizontalMargin,
|
||||
y: thisObject.container.frame.height + offsetY
|
||||
}
|
||||
|
||||
/* Now the transformations. */
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1)
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2)
|
||||
|
||||
point1 = thisObject.fitFunction(point1)
|
||||
point2 = thisObject.fitFunction(point2)
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.strokeStyle = progressBar.strokeStyle.replace('@Opacity', progressBar.opacity.toString())
|
||||
|
||||
browserCanvasContext.setLineDash([1, 4])
|
||||
browserCanvasContext.lineWidth = lineWidth
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
|
||||
function drawLayerDisplay () {
|
||||
let baseAsset = thisObject.definition.referenceParent.parentNode.referenceParent.baseAsset.referenceParent
|
||||
let quotedAsset = thisObject.definition.referenceParent.parentNode.referenceParent.quotedAsset.referenceParent
|
||||
let market = baseAsset.code.codeName + '/' + quotedAsset.code.codeName
|
||||
let exchange = thisObject.definition.referenceParent.parentNode.referenceParent.parentNode.parentNode
|
||||
let plotterModule = thisObject.definition.referenceParent.referenceParent.referenceParent
|
||||
|
||||
let label1 = thisObject.payload.node.name
|
||||
let label2 = exchange.name.substring(0, 15) + ' - ' + market
|
||||
let label3 = thisObject.status.toUpperCase()
|
||||
|
||||
if (label1 !== undefined) {
|
||||
label1 = label1.substring(0, 22)
|
||||
}
|
||||
|
||||
let icon1 = getIcon(exchange)
|
||||
|
||||
let icon2
|
||||
if (plotterModule.code.icon !== undefined) {
|
||||
icon2 = canvas.designSpace.iconCollection.get(plotterModule.code.icon)
|
||||
}
|
||||
|
||||
let icon3 = getIcon(baseAsset)
|
||||
let icon4 = getIcon(quotedAsset)
|
||||
|
||||
function getIcon (node) {
|
||||
let nodeDefinition = APP_SCHEMA_MAP.get(node.type)
|
||||
let iconName
|
||||
if (nodeDefinition.alternativeIcons !== undefined) {
|
||||
for (let i = 0; i < nodeDefinition.alternativeIcons.length; i++) {
|
||||
alternativeIcon = nodeDefinition.alternativeIcons[i]
|
||||
if (alternativeIcon.codeName === node.code.codeName) {
|
||||
iconName = alternativeIcon.iconName
|
||||
}
|
||||
}
|
||||
}
|
||||
if (iconName !== undefined) {
|
||||
icon = canvas.designSpace.iconCollection.get(iconName)
|
||||
} else {
|
||||
icon = canvas.designSpace.iconCollection.get(nodeDefinition.icon)
|
||||
}
|
||||
return icon
|
||||
}
|
||||
|
||||
let backgroundColor = UI_COLOR.BLACK
|
||||
|
||||
const RED_LINE_HIGHT = 4
|
||||
const OPACITY = 0.75
|
||||
|
||||
let params = {
|
||||
cornerRadius: 0,
|
||||
lineWidth: 1,
|
||||
container: thisObject.container,
|
||||
borderColor: UI_COLOR.RUSTED_RED,
|
||||
castShadow: false,
|
||||
backgroundColor: backgroundColor,
|
||||
opacity: OPACITY
|
||||
}
|
||||
|
||||
roundedCornersBackground(params)
|
||||
|
||||
drawLabel(label1, 1 / 2, 6 / 10, -5, 0, 17, thisObject.container)
|
||||
drawLabel(label2, 1 / 2, 8.2 / 10, -5, 0, 9, thisObject.container)
|
||||
drawLabel(label3, 1 / 2, 9.5 / 10, -5, 0, 9, thisObject.container)
|
||||
|
||||
drawProgressBar(marketFileProgressBar, 1, -45)
|
||||
drawProgressBar(dailyFileProgressBar, 1, -46)
|
||||
drawProgressBar(singleFileProgressBar, 1, -47)
|
||||
drawProgressBar(fileSequenceProgressBar, 1, -48)
|
||||
|
||||
drawIcon(icon1, 1 / 8, 2 / 10, 0, 0, 14, thisObject.container)
|
||||
drawIcon(icon2, 7 / 8, 2 / 10, 0, 0, 14, thisObject.container)
|
||||
drawIcon(icon3, 3.4 / 8, 2 / 10, 0, 0, 14, thisObject.container)
|
||||
drawIcon(icon4, 4.6 / 8, 2 / 10, 0, 0, 14, thisObject.container)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,458 @@
|
||||
function newLayersPanel () {
|
||||
let thisObject = {
|
||||
name: 'Layers Panel',
|
||||
fitFunction: undefined,
|
||||
container: undefined,
|
||||
layers: [],
|
||||
payload: undefined,
|
||||
isVisible: true,
|
||||
panelTabButton: undefined,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize()
|
||||
thisObject.container.isDraggeable = true
|
||||
thisObject.container.isWheelable = true
|
||||
|
||||
let isInitialized = false
|
||||
|
||||
let layersMap = new Map()
|
||||
let visibleLayers = []
|
||||
let firstVisibleLayer = 1
|
||||
|
||||
const LAYER_SEPARATION = 0
|
||||
|
||||
let headerHeight = 40
|
||||
let footerHeight = 10
|
||||
let layerHeight = 70
|
||||
let desiredVisibleLayers = 5
|
||||
let posibleVisibleLayers = 5
|
||||
let desiredPanelHeight = (layerHeight + LAYER_SEPARATION) * desiredVisibleLayers + headerHeight + footerHeight
|
||||
let posiblePanelHeight = (layerHeight + LAYER_SEPARATION) * posibleVisibleLayers + headerHeight + footerHeight
|
||||
|
||||
let onMouseWheelEventSuscriptionId
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(onMouseWheelEventSuscriptionId)
|
||||
|
||||
thisObject.panelTabButton.finalize()
|
||||
thisObject.panelTabButton = undefined
|
||||
layersMap = undefined
|
||||
visibleLayers = undefined
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
|
||||
thisObject.payload = undefined
|
||||
thisObject = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
thisObject.container.name = thisObject.payload.node.name
|
||||
thisObject.container.frame.containerName = thisObject.container.name
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL
|
||||
thisObject.container.frame.height = headerHeight
|
||||
|
||||
let position = { // Default position
|
||||
x: canvas.chartingSpace.viewport.visibleArea.topLeft.x,
|
||||
y: canvas.chartingSpace.viewport.visibleArea.topLeft.y
|
||||
}
|
||||
|
||||
thisObject.container.frame.position = position
|
||||
loadFrame(thisObject.payload, thisObject.container.frame)
|
||||
|
||||
thisObject.panelTabButton = newPanelTabButton()
|
||||
thisObject.panelTabButton.parentContainer = thisObject.container
|
||||
thisObject.panelTabButton.container.frame.parentFrame = thisObject.container.frame
|
||||
thisObject.panelTabButton.fitFunction = thisObject.fitFunction
|
||||
thisObject.panelTabButton.initialize()
|
||||
|
||||
onMouseWheelEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel)
|
||||
|
||||
readObjectState()
|
||||
|
||||
isInitialized = true
|
||||
}
|
||||
|
||||
function saveObjectStateVisibleLayers () {
|
||||
savePropertyAtNodeConfig(thisObject.payload, 'visibleLayers', desiredVisibleLayers)
|
||||
}
|
||||
|
||||
function saveObjectStatePanelLocation () {
|
||||
savePropertyAtNodeConfig(thisObject.payload, 'panelLocation', thisObject.panelTabButton.status)
|
||||
}
|
||||
|
||||
function readObjectState () {
|
||||
let storedValue
|
||||
|
||||
storedValue = loadPropertyFromNodeConfig(thisObject.payload, 'visibleLayers')
|
||||
|
||||
if (isNaN(storedValue) || storedValue === null || storedValue === undefined) {
|
||||
// not using this value
|
||||
saveObjectStateVisibleLayers() // this overrides any invalid value at the config.
|
||||
return
|
||||
} else {
|
||||
if (storedValue < 0) { storedValue = 0 }
|
||||
if (storedValue > thisObject.layers.length) {
|
||||
storedValue = thisObject.layers.length
|
||||
}
|
||||
|
||||
if (storedValue !== desiredVisibleLayers) {
|
||||
desiredVisibleLayers = storedValue
|
||||
desiredPanelHeight = (layerHeight + LAYER_SEPARATION) * desiredVisibleLayers + headerHeight + footerHeight
|
||||
calculateVisbleLayers()
|
||||
}
|
||||
}
|
||||
|
||||
storedValue = loadPropertyFromNodeConfig(thisObject.payload, 'panelLocation')
|
||||
|
||||
if (storedValue === null || storedValue === undefined) {
|
||||
// not using this value
|
||||
saveObjectStatePanelLocation() // this overrides any invalid value at the config.
|
||||
return
|
||||
} else {
|
||||
if (storedValue !== 'up' && storedValue !== 'down') {
|
||||
saveObjectStatePanelLocation() // this overrides any invalid value at the config.
|
||||
return
|
||||
}
|
||||
thisObject.panelTabButton.setStatus(storedValue)
|
||||
saveObjectStatePanelLocation()
|
||||
}
|
||||
}
|
||||
|
||||
function removeLayer (id) {
|
||||
let layer = layersMap.get(id)
|
||||
layersMap.delete(id)
|
||||
layer.turnOff()
|
||||
layer.container.eventHandler.stopListening(layer.onLayerStatusChangedEventSuscriptionId)
|
||||
layer.container.eventHandler.stopListening(layer.onMouseWheelEventSuscriptionId)
|
||||
layer.finalize()
|
||||
}
|
||||
|
||||
function addLayer (layerNode) {
|
||||
/* Now we create Product objects */
|
||||
let layer = newLayer()
|
||||
layer.payload = layerNode.payload
|
||||
layer.nodeId = layerNode.payload.node.id
|
||||
layer.fitFunction = thisObject.fitFunction
|
||||
|
||||
/* Initialize it */
|
||||
layer.initialize(onInitialized)
|
||||
|
||||
function onInitialized (err) {
|
||||
if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) {
|
||||
layersMap.set(layerNode.id, layer)
|
||||
|
||||
/* Container Stuff */
|
||||
layer.container.frame.parentFrame = thisObject.container.frame
|
||||
layer.container.parentContainer = thisObject.container
|
||||
layer.container.isWheelable = true
|
||||
|
||||
/* Add to the Product Array */
|
||||
thisObject.layers.push(layer)
|
||||
|
||||
/* Listen to Status Changes Events */
|
||||
layer.onLayerStatusChangedEventSuscriptionId = layer.container.eventHandler.listenToEvent('Status Changed', onLayerStatusChanged)
|
||||
layer.onMouseWheelEventSuscriptionId = layer.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel)
|
||||
|
||||
return layer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseWheel (event) {
|
||||
delta = event.wheelDelta
|
||||
if (delta > 0) {
|
||||
delta = -1
|
||||
} else {
|
||||
delta = 1
|
||||
}
|
||||
|
||||
if (event.y - thisObject.container.frame.position.y - CURRENT_TOP_MARGIN < headerHeight) { // Mouse wheel over the header, not a layer
|
||||
desiredVisibleLayers = desiredVisibleLayers + delta
|
||||
if (desiredVisibleLayers < 0) { desiredVisibleLayers = 0 }
|
||||
if (desiredVisibleLayers > thisObject.layers.length) { desiredVisibleLayers = thisObject.layers.length }
|
||||
} else {
|
||||
firstVisibleLayer = firstVisibleLayer + delta
|
||||
}
|
||||
desiredPanelHeight = (layerHeight + LAYER_SEPARATION) * desiredVisibleLayers + headerHeight + footerHeight
|
||||
calculateVisbleLayers()
|
||||
saveObjectStateVisibleLayers()
|
||||
}
|
||||
|
||||
function panelSizePhysics () {
|
||||
if (isInitialized === false || thisObject.visible === false || thisObject.isHidden === true) { return }
|
||||
|
||||
let viewPortHeight = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y
|
||||
|
||||
if (viewPortHeight < headerHeight) {
|
||||
thisObject.visible = false
|
||||
} else {
|
||||
thisObject.visible = true
|
||||
}
|
||||
|
||||
if (desiredPanelHeight > viewPortHeight) {
|
||||
posibleVisibleLayers = Math.trunc((viewPortHeight - headerHeight - footerHeight) / (layerHeight + LAYER_SEPARATION))
|
||||
} else {
|
||||
posibleVisibleLayers = desiredVisibleLayers
|
||||
}
|
||||
if (thisObject.layers.length < posibleVisibleLayers) { posibleVisibleLayers = thisObject.layers.length }
|
||||
|
||||
if (posibleVisibleLayers === 0) {
|
||||
posiblePanelHeight = (layerHeight + LAYER_SEPARATION) * posibleVisibleLayers + headerHeight
|
||||
} else {
|
||||
posiblePanelHeight = (layerHeight + LAYER_SEPARATION) * posibleVisibleLayers + headerHeight + footerHeight
|
||||
}
|
||||
|
||||
thisObject.container.frame.height = posiblePanelHeight
|
||||
}
|
||||
|
||||
function calculateVisbleLayers () {
|
||||
panelSizePhysics()
|
||||
let availableSlots = posibleVisibleLayers
|
||||
|
||||
if (firstVisibleLayer < 1) { firstVisibleLayer = 1 }
|
||||
if (firstVisibleLayer > (thisObject.layers.length - availableSlots + 1)) { firstVisibleLayer = thisObject.layers.length - availableSlots + 1 }
|
||||
|
||||
visibleLayers = []
|
||||
|
||||
for (let i = 0; i < thisObject.layers.length; i++) {
|
||||
if (i + 1 >= firstVisibleLayer && i + 1 < firstVisibleLayer + availableSlots) {
|
||||
let layer = thisObject.layers[i]
|
||||
|
||||
layer.container.frame.position.x = 0
|
||||
layer.container.frame.position.y = (layerHeight + LAYER_SEPARATION) * visibleLayers.length + headerHeight
|
||||
|
||||
/* Add to Visible Product Array */
|
||||
visibleLayers.push(layer)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onLayerStatusChanged (layer) {
|
||||
thisObject.container.eventHandler.raiseEvent('Layer Status Changed', layer)
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if (isInitialized === false || thisObject.visible === false || thisObject.isHidden === true) { return }
|
||||
let container
|
||||
|
||||
container = thisObject.panelTabButton.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
|
||||
/* First we check if thisObject point is inside thisObject space. */
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
/* Now we see which is the inner most container that has it */
|
||||
for (let i = 0; i < visibleLayers.length; i++) {
|
||||
container = visibleLayers[i].getContainer(point)
|
||||
|
||||
if (container !== undefined) {
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
checkPoint = thisObject.fitFunction(checkPoint)
|
||||
|
||||
if (point.x === checkPoint.x && point.y === checkPoint.y) {
|
||||
return container
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* The point does not belong to any inner container, so we return the current container. */
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
checkPoint = thisObject.fitFunction(checkPoint)
|
||||
|
||||
if (point.x === checkPoint.x && point.y === checkPoint.y) {
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function childrenPhysics () {
|
||||
for (let i = 0; i < thisObject.layers.length; i++) {
|
||||
let layer = thisObject.layers[i]
|
||||
layer.physics()
|
||||
}
|
||||
}
|
||||
|
||||
function syncWithConfigPhysics () {
|
||||
readObjectState()
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (isInitialized === false) { return }
|
||||
thisObject.panelTabButton.physics()
|
||||
saveFrame(thisObject.payload, thisObject.container.frame)
|
||||
syncWithConfigPhysics()
|
||||
|
||||
/*
|
||||
The overall idea here is that we need to keep syncronized the panel with the layers that are
|
||||
defined at the Designer. Users can connect or disconnect any objext resulting in changes in which
|
||||
are valid layers and which not at any point in time. So what we do here is trying to keep the panel
|
||||
only with the layers that are connected to the Charting Space hiriarcy.
|
||||
|
||||
To achieve this, first we are going to move all session related cards to a local array. Then we are
|
||||
going to check for layers at the designer, and will move back the cards which still have layers well
|
||||
defined to where they were.
|
||||
|
||||
If we find new layers we will add them at that point. Finally, the cards that still remain at the
|
||||
local array after all the layers at the designer have been processed, are turned off and discarded.
|
||||
*/
|
||||
childrenPhysics()
|
||||
|
||||
let localLayers = []
|
||||
moveToLocalLayers()
|
||||
syncWithDesignerLayers()
|
||||
|
||||
/* At this poins all the cards still at the local array need to be removed from the panel. */
|
||||
turnOffUnusedLayers()
|
||||
calculateVisbleLayers()
|
||||
|
||||
function syncWithDesignerLayers () {
|
||||
let layerManager = thisObject.payload.node
|
||||
for (let p = 0; p < layerManager.layers.length; p++) {
|
||||
let layerNode = layerManager.layers[p]
|
||||
|
||||
let found = removeFromLocalLayers(layerNode.id)
|
||||
if (found !== true) {
|
||||
layer = addLayer(layerNode)
|
||||
if (layer !== undefined) {
|
||||
onLayerStatusChanged(layer)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function moveToLocalLayers () {
|
||||
removeNext()
|
||||
|
||||
function removeNext () {
|
||||
for (let i = 0; i < thisObject.layers.length; i++) {
|
||||
let layer = thisObject.layers[i]
|
||||
|
||||
thisObject.layers.splice(i, 1)
|
||||
localLayers.push(layer)
|
||||
removeNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function removeFromLocalLayers (id) {
|
||||
for (let i = 0; i < localLayers.length; i++) {
|
||||
let layer = localLayers[i]
|
||||
if (layer.payload.node.id === id) {
|
||||
thisObject.layers.push(layer)
|
||||
localLayers.splice(i, 1)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function turnOffUnusedLayers () {
|
||||
for (let i = 0; i < localLayers.length; i++) {
|
||||
let layer = localLayers[i]
|
||||
removeLayer(layer.nodeId)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (isInitialized === false || thisObject.visible === false || thisObject.isHidden === true) { return }
|
||||
|
||||
// thisObject.container.frame.draw(false, false, false, thisObject.fitFunction)
|
||||
|
||||
drawHeader()
|
||||
for (let i = 0; i < visibleLayers.length; i++) {
|
||||
visibleLayers[i].draw()
|
||||
}
|
||||
drawScrollBar()
|
||||
thisObject.panelTabButton.draw()
|
||||
}
|
||||
|
||||
function drawHeader () {
|
||||
let label1 = thisObject.payload.node.payload.parentNode.payload.parentNode.name.substring(0, 18)
|
||||
let label2 = thisObject.payload.node.payload.parentNode.name.substring(0, 18)
|
||||
let label3 = ''
|
||||
|
||||
let icon1 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.payload.parentNode.type)
|
||||
let icon2 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.type)
|
||||
|
||||
let backgroundColor = UI_COLOR.BLACK
|
||||
|
||||
let params = {
|
||||
cornerRadius: 5,
|
||||
lineWidth: 1,
|
||||
container: thisObject.container,
|
||||
borderColor: UI_COLOR.RUSTED_RED,
|
||||
castShadow: false,
|
||||
backgroundColor: backgroundColor,
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
roundedCornersBackground(params)
|
||||
|
||||
drawLabel(label1, 1 / 2, 0, 0, 15, 9, thisObject.container)
|
||||
drawLabel(label2, 1 / 2, 0, 0, 30, 9, thisObject.container)
|
||||
|
||||
drawIcon(icon1, 1 / 8, 0, 0, 20, 28, thisObject.container)
|
||||
drawIcon(icon2, 7 / 8, 0, 0, 20, 28, thisObject.container)
|
||||
}
|
||||
|
||||
function drawScrollBar () {
|
||||
if (thisObject.layers.length > posibleVisibleLayers && posibleVisibleLayers > 0) {
|
||||
let xOffset = 4
|
||||
let barTopPoint = {
|
||||
x: thisObject.container.frame.width - xOffset,
|
||||
y: headerHeight
|
||||
}
|
||||
let barBottomPoint = {
|
||||
x: thisObject.container.frame.width - xOffset,
|
||||
y: thisObject.container.frame.height - footerHeight
|
||||
}
|
||||
let ratio = posibleVisibleLayers / thisObject.layers.length
|
||||
let handleHeight = (posibleVisibleLayers * (layerHeight + LAYER_SEPARATION)) * ratio
|
||||
let handleTopPoint = {
|
||||
x: thisObject.container.frame.width - xOffset,
|
||||
y: headerHeight + (layerHeight + LAYER_SEPARATION) * ratio * (firstVisibleLayer - 1)
|
||||
}
|
||||
let handleBottomPoint = {
|
||||
x: thisObject.container.frame.width - xOffset,
|
||||
y: handleTopPoint.y + handleHeight
|
||||
}
|
||||
barTopPoint = thisObject.container.frame.frameThisPoint(barTopPoint)
|
||||
barBottomPoint = thisObject.container.frame.frameThisPoint(barBottomPoint)
|
||||
handleTopPoint = thisObject.container.frame.frameThisPoint(handleTopPoint)
|
||||
handleBottomPoint = thisObject.container.frame.frameThisPoint(handleBottomPoint)
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(barTopPoint.x, barTopPoint.y)
|
||||
browserCanvasContext.lineTo(barBottomPoint.x, barBottomPoint.y)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.GREY + ', ' + 1 + ')'
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(handleTopPoint.x, handleTopPoint.y)
|
||||
browserCanvasContext.lineTo(handleBottomPoint.x, handleBottomPoint.y)
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
browserCanvasContext.lineWidth = 4
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + 1 + ')'
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
function newPanelTabButton () {
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
parentContainer: undefined,
|
||||
status: undefined,
|
||||
setStatus: setStatus,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let isInitialized = false
|
||||
/* Cointainer stuff */
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.name = 'Panel Tab Button'
|
||||
thisObject.container.initialize()
|
||||
thisObject.container.isClickeable = true
|
||||
thisObject.container.frame.containerName = 'Panel Tab Button'
|
||||
|
||||
/* Animation to hide the pannel */
|
||||
thisObject.status = 'up'
|
||||
let upPosition = {}
|
||||
let downPosition = {}
|
||||
let transitionPosition = {}
|
||||
|
||||
let onMouseClickEventSuscriptionId
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(onMouseClickEventSuscriptionId)
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.parentContainer = undefined
|
||||
|
||||
thisObject.status = undefined
|
||||
upPosition = undefined
|
||||
downPosition = undefined
|
||||
transitionPosition = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
thisObject.container.frame.width = 10
|
||||
thisObject.container.frame.height = 10
|
||||
|
||||
var position = {
|
||||
x: thisObject.parentContainer.frame.width - 14,
|
||||
y: 1
|
||||
}
|
||||
|
||||
thisObject.container.frame.position = position
|
||||
|
||||
upPosition.x = thisObject.parentContainer.frame.position.x
|
||||
upPosition.y = thisObject.parentContainer.frame.position.y
|
||||
|
||||
downPosition.x = thisObject.parentContainer.frame.position.x
|
||||
downPosition.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y
|
||||
|
||||
transitionPosition.x = upPosition.x
|
||||
transitionPosition.y = upPosition.y
|
||||
|
||||
/* Lets listen to our own events to react when we have a Mouse Click */
|
||||
onMouseClickEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
|
||||
|
||||
isInitialized = true
|
||||
}
|
||||
|
||||
function setStatus (value) {
|
||||
if (value === 'up' && thisObject.status === 'down') {
|
||||
thisObject.status = 'going up'
|
||||
}
|
||||
if (value === 'down' && thisObject.status === 'up') {
|
||||
thisObject.status = 'going down'
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseClick (event) {
|
||||
if (thisObject.status === 'up') {
|
||||
transitionPosition.x = upPosition.x
|
||||
transitionPosition.y = upPosition.y
|
||||
|
||||
thisObject.status = 'going down'
|
||||
} else {
|
||||
transitionPosition.x = downPosition.x
|
||||
transitionPosition.y = downPosition.y
|
||||
|
||||
thisObject.status = 'going up'
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (isInitialized === false) { return }
|
||||
|
||||
upPosition.x = thisObject.parentContainer.frame.position.x
|
||||
upPosition.y = canvas.chartingSpace.viewport.visibleArea.topRight.y
|
||||
|
||||
downPosition.x = thisObject.parentContainer.frame.position.x
|
||||
downPosition.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y
|
||||
|
||||
if (thisObject.status === 'going down') {
|
||||
if (transitionPosition.y < downPosition.y) {
|
||||
transitionPosition.y = transitionPosition.y + 50
|
||||
thisObject.parentContainer.frame.position.y = transitionPosition.y
|
||||
} else {
|
||||
thisObject.parentContainer.frame.position.y = downPosition.y
|
||||
thisObject.status = 'down'
|
||||
}
|
||||
}
|
||||
if (thisObject.status === 'going up') {
|
||||
if (transitionPosition.y > upPosition.y) {
|
||||
transitionPosition.y = transitionPosition.y - 50
|
||||
thisObject.parentContainer.frame.position.y = transitionPosition.y
|
||||
} else {
|
||||
thisObject.parentContainer.frame.position.y = upPosition.y
|
||||
thisObject.status = 'up'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (isInitialized === false) { return }
|
||||
|
||||
drawButton()
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
|
||||
/* First we check if thisObject point is inside thisObject space. */
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
/* Now we see which is the inner most container that has it */
|
||||
|
||||
return thisObject.container
|
||||
} else {
|
||||
/* This point does not belong to thisObject space. */
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function drawButton () {
|
||||
let point1
|
||||
let point2
|
||||
let point3
|
||||
|
||||
switch (thisObject.status) {
|
||||
case 'up': {
|
||||
point1 = {
|
||||
x: 3,
|
||||
y: 3
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: thisObject.container.frame.width - 3,
|
||||
y: 3
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: thisObject.container.frame.width / 2,
|
||||
y: thisObject.container.frame.height - 3
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'going down': {
|
||||
point1 = {
|
||||
x: 4,
|
||||
y: 4
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: thisObject.container.frame.width - 4,
|
||||
y: 4
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: thisObject.container.frame.width / 2,
|
||||
y: thisObject.container.frame.height - 4
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'going up': {
|
||||
point1 = {
|
||||
x: 4,
|
||||
y: thisObject.container.frame.height - 4
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: thisObject.container.frame.width - 4,
|
||||
y: thisObject.container.frame.height - 4
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: thisObject.container.frame.width / 2,
|
||||
y: 4
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
case 'down': {
|
||||
point1 = {
|
||||
x: 3,
|
||||
y: thisObject.container.frame.height - 3
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: thisObject.container.frame.width - 3,
|
||||
y: thisObject.container.frame.height - 3
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: thisObject.container.frame.width / 2,
|
||||
y: 3
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
/* Now the transformations. */
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1)
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2)
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3)
|
||||
|
||||
if (thisObject.fitFunction !== undefined) {
|
||||
point1 = thisObject.fitFunction(point1)
|
||||
point2 = thisObject.fitFunction(point2)
|
||||
point3 = thisObject.fitFunction(point3)
|
||||
}
|
||||
|
||||
/* Lets start the drawing. */
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
browserCanvasContext.lineTo(point3.x, point3.y)
|
||||
browserCanvasContext.lineTo(point1.x, point1.y)
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.strokeStyle = 'rgba(250, 250, 250, 1)'
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(250, 250, 250, 1)'
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
function newPanelsVisibleButton () {
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
parentContainer: undefined,
|
||||
panels: undefined,
|
||||
showPanels: true,
|
||||
draw: draw,
|
||||
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
/* Cointainer stuff */
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.name = 'Panels Visible Button'
|
||||
thisObject.container.initialize()
|
||||
thisObject.container.isClickeable = true
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.frame.containerName = 'Panels Visible Button'
|
||||
|
||||
let onMouseClickEventSuscriptionId
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(onMouseClickEventSuscriptionId)
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.parentContainer = undefined
|
||||
thisObject.panels = undefined
|
||||
}
|
||||
|
||||
function initialize (panels) {
|
||||
thisObject.panels = panels
|
||||
setPanelVisibility()
|
||||
|
||||
thisObject.container.frame.width = 25
|
||||
thisObject.container.frame.height = 25
|
||||
|
||||
let position = {
|
||||
x: thisObject.container.frame.width * 1 / 8,
|
||||
y: 45
|
||||
}
|
||||
|
||||
thisObject.container.frame.position = position
|
||||
|
||||
/* Lets listen to our own events to react when we have a Mouse Click */
|
||||
onMouseClickEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
|
||||
}
|
||||
|
||||
function onMouseClick (event) {
|
||||
if (thisObject.showPanels === false) {
|
||||
thisObject.showPanels = true
|
||||
} else {
|
||||
thisObject.showPanels = false
|
||||
}
|
||||
setPanelVisibility()
|
||||
}
|
||||
|
||||
function setPanelVisibility () {
|
||||
for (let i = 0; i < thisObject.panels.length; i++) {
|
||||
panel = canvas.panelsSpace.getPanel(thisObject.panels[i])
|
||||
panel.isVisible = thisObject.showPanels
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
let icon
|
||||
if (thisObject.showPanels === true) {
|
||||
icon = canvas.designSpace.iconCollection.get('plotter-panel')
|
||||
} else {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-panel-off')
|
||||
}
|
||||
|
||||
drawIcon(icon, 1 / 2, 1 / 2, 0, 0, 14, thisObject.container)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,185 @@
|
||||
|
||||
function newPlotterPanel () {
|
||||
const MODULE_NAME = 'Plotter Panel'
|
||||
const ERROR_LOG = true
|
||||
const INTENSIVE_LOG = false
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
fitFunction: undefined,
|
||||
container: undefined,
|
||||
isVisible: true,
|
||||
onRecordChange: onRecordChange,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize('Plotter Panel')
|
||||
|
||||
let heightFactor = 1
|
||||
let currentRecord
|
||||
let panelTabButton
|
||||
let panelNode
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.isVisible = undefined
|
||||
|
||||
heightFactor = undefined
|
||||
currentRecord = undefined
|
||||
panelTabButton.finalize()
|
||||
panelTabButton = undefined
|
||||
panelNode = undefined
|
||||
}
|
||||
|
||||
function initialize (pPanelNode) {
|
||||
panelNode = pPanelNode
|
||||
thisObject.container.frame.containerName = panelNode.name
|
||||
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL
|
||||
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL
|
||||
|
||||
thisObject.container.frame.position.x = canvas.chartingSpace.viewport.visibleArea.topRight.x - thisObject.container.frame.width - thisObject.container.frame.width * Math.random() * 8
|
||||
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y - thisObject.container.frame.height - thisObject.container.frame.height * Math.random() * 1.5
|
||||
|
||||
panelTabButton = newPanelTabButton()
|
||||
panelTabButton.parentContainer = thisObject.container
|
||||
panelTabButton.container.frame.parentFrame = thisObject.container.frame
|
||||
panelTabButton.fitFunction = thisObject.fitFunction
|
||||
panelTabButton.initialize()
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
let container
|
||||
|
||||
container = panelTabButton.getContainer(point)
|
||||
if (container !== undefined) { return container }
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
let checkPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
checkPoint = thisObject.fitFunction(checkPoint)
|
||||
|
||||
if (point.x === checkPoint.x && point.y === checkPoint.y) {
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onRecordChange (pCurrentRecord) {
|
||||
currentRecord = pCurrentRecord
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.isVisible !== true) { return }
|
||||
|
||||
thisObject.container.frame.draw(false, false, true, thisObject.fitFunction)
|
||||
plotCurrentRecordData()
|
||||
panelTabButton.draw()
|
||||
}
|
||||
|
||||
function plotCurrentRecordData () {
|
||||
const frameBodyHeight = thisObject.container.frame.getBodyHeight()
|
||||
const frameTitleHeight = thisObject.container.frame.height - frameBodyHeight
|
||||
|
||||
const X_AXIS = thisObject.container.frame.width / 2
|
||||
const Y_AXIS = frameTitleHeight + frameBodyHeight / 2
|
||||
const PANEL_HEIGHT = frameTitleHeight + frameBodyHeight
|
||||
|
||||
if (currentRecord === undefined) { return }
|
||||
if (currentRecord.data === undefined) { return }
|
||||
|
||||
let record = currentRecord.data
|
||||
|
||||
/* First we execute code if provided. */
|
||||
if (panelNode.javascriptCode !== undefined) {
|
||||
try {
|
||||
eval(panelNode.javascriptCode.code)
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] plotCurrentRecordData -> err = ' + err.stack) }
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] plotCurrentRecordData -> javascriptCode.code = ' + panelNode.javascriptCode.code) }
|
||||
}
|
||||
}
|
||||
|
||||
/* Second we go through the panel data. */
|
||||
if (panelNode.panelData === undefined) { return }
|
||||
for (let i = 0; i < panelNode.panelData.length; i++) {
|
||||
let panelData = panelNode.panelData[i]
|
||||
|
||||
let labelText = panelData.name
|
||||
let labelPosition = i * 10 + 10
|
||||
let valuePosition = i * 10 + 15
|
||||
let value = 'No value defined.'
|
||||
|
||||
if (valuePosition > 100) {
|
||||
heightFactor = 2
|
||||
thisObject.container.frame.height = UI_PANEL.HEIGHT.NORMAL * heightFactor
|
||||
}
|
||||
|
||||
if (panelData.dataFormula !== undefined) {
|
||||
try {
|
||||
value = eval(panelData.dataFormula.code)
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] plotCurrentRecordData -> err = ' + err.stack) }
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] plotCurrentRecordData -> dataFormula.code = ' + panelNode.dataFormula.code) }
|
||||
}
|
||||
}
|
||||
|
||||
if (panelData.code.valueDecimals !== undefined) {
|
||||
value = value.toFixed(panelData.code.valueDecimals)
|
||||
}
|
||||
|
||||
if (panelData.code.labelText !== undefined) {
|
||||
labelText = panelData.code.labelText
|
||||
}
|
||||
|
||||
if (panelData.code.labelPosition !== undefined) {
|
||||
labelPosition = panelData.code.labelPosition
|
||||
}
|
||||
|
||||
if (panelData.code.valuePosition !== undefined) {
|
||||
valuePosition = panelData.code.valuePosition
|
||||
}
|
||||
|
||||
printLabel(labelText, X_AXIS, PANEL_HEIGHT * labelPosition / 100 / heightFactor, '1')
|
||||
printLabel(value, X_AXIS, PANEL_HEIGHT * valuePosition / 100 / heightFactor, '0.50')
|
||||
}
|
||||
|
||||
function printLabel (labelToPrint, x, y, opacity) {
|
||||
let labelPoint
|
||||
let fontSize = 10
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.SECONDARY
|
||||
|
||||
let label = '' + labelToPrint
|
||||
|
||||
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO
|
||||
|
||||
labelPoint = {
|
||||
x: x - xOffset,
|
||||
y: y
|
||||
}
|
||||
|
||||
labelPoint = thisObject.container.frame.frameThisPoint(labelPoint)
|
||||
labelPoint = thisObject.fitFunction(labelPoint)
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK + ', ' + opacity + ')'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,821 @@
|
||||
function newPlotter () {
|
||||
const MODULE_NAME = 'Plotter'
|
||||
const ERROR_LOG = true
|
||||
const INTENSIVE_LOG = false
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
currentRecord: undefined,
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
onDailyFileLoaded: onDailyFileLoaded,
|
||||
initialize: initialize,
|
||||
finalize: finalize,
|
||||
getContainer: getContainer,
|
||||
setTimeFrame: setTimeFrame,
|
||||
setDatetime: setDatetime,
|
||||
setCoordinateSystem: setCoordinateSystem,
|
||||
draw: draw
|
||||
}
|
||||
|
||||
let container = newContainer()
|
||||
container.initialize()
|
||||
thisObject.container = container
|
||||
|
||||
let coordinateSystem
|
||||
let plotterModuleConfig
|
||||
let slotHeight = (canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topLeft.y) / 10 // This is the amount of slots available
|
||||
let mustRecalculateDataPoints = false
|
||||
let atMousePositionFillStyles = new Map()
|
||||
let atMousePositionStrokeStyles = new Map()
|
||||
|
||||
let timeFrame // This will hold the current Time Frame the user is at.
|
||||
let datetime // This will hold the current Datetime the user is at.
|
||||
|
||||
let marketFile // This is the current Market File being plotted.
|
||||
let fileCursor // This is the current File Cursor being used to retrieve Daily Files.
|
||||
|
||||
let marketFiles // This object will provide the different Market Files at different Time Periods.
|
||||
let dailyFiles // This object will provide the different File Cursors at different Time Periods.
|
||||
|
||||
let productDefinition // Here we store a snapshot of the product definition which references this plotter.
|
||||
|
||||
let records = [] // We will have the information to be plotted here.
|
||||
let userPositionDate
|
||||
|
||||
let onMouseOverEventSuscriptionId
|
||||
let zoomChangedEventSubscriptionId
|
||||
let offsetChangedEventSubscriptionId
|
||||
let dragFinishedEventSubscriptionId
|
||||
let dimmensionsChangedEventSubscriptionId
|
||||
let marketFilesUpdatedEventSubscriptionId
|
||||
let dailyFilesUpdatedEventSubscriptionId
|
||||
let onDisplaceEventSubscriptionId
|
||||
let scaleChangedEventSubscriptionId
|
||||
|
||||
let logged = false
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
try {
|
||||
/* Stop listening to the necesary events. */
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(zoomChangedEventSubscriptionId)
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(offsetChangedEventSubscriptionId)
|
||||
canvas.eventHandler.stopListening(dragFinishedEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(dimmensionsChangedEventSubscriptionId)
|
||||
marketFiles.eventHandler.stopListening(marketFilesUpdatedEventSubscriptionId)
|
||||
dailyFiles.eventHandler.stopListening(dailyFilesUpdatedEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onDisplaceEventSubscriptionId)
|
||||
thisObject.fitFunction = undefined
|
||||
|
||||
/* Clear References */
|
||||
marketFiles = undefined
|
||||
dailyFiles = undefined
|
||||
|
||||
datetime = undefined
|
||||
timeFrame = undefined
|
||||
|
||||
marketFile = undefined
|
||||
fileCursor = undefined
|
||||
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = undefined
|
||||
plotterModuleConfig = undefined
|
||||
slotHeight = undefined
|
||||
mustRecalculateDataPoints = undefined
|
||||
atMousePositionFillStyles = undefined
|
||||
atMousePositionStrokeStyles = undefined
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function initialize (pStorage, pDatetime, pTimeFrame, pCoordinateSystem, callBackFunction, pProductDefinition) {
|
||||
try {
|
||||
/* Store the information received. */
|
||||
marketFiles = pStorage.marketFiles[0]
|
||||
dailyFiles = pStorage.dailyFiles[0]
|
||||
|
||||
datetime = pDatetime
|
||||
timeFrame = pTimeFrame
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
|
||||
productDefinition = pProductDefinition
|
||||
|
||||
/* We need a Market File in order to calculate the Y scale, since this scale depends on actual data. */
|
||||
marketFile = marketFiles.getFile(ONE_DAY_IN_MILISECONDS) // This file is the one processed faster.
|
||||
|
||||
/* Now we set the right files according to current Period. */
|
||||
marketFile = marketFiles.getFile(pTimeFrame)
|
||||
fileCursor = dailyFiles.getFileCursor(pTimeFrame)
|
||||
|
||||
/* Listen to the necesary events. */
|
||||
zoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Zoom Changed', onViewportZoomChanged)
|
||||
offsetChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Position Changed', onViewportPositionChanged)
|
||||
dragFinishedEventSubscriptionId = canvas.eventHandler.listenToEvent('Drag Finished', onDragFinished)
|
||||
marketFilesUpdatedEventSubscriptionId = marketFiles.eventHandler.listenToEvent('Files Updated', onMarketFilesUpdated)
|
||||
dailyFilesUpdatedEventSubscriptionId = dailyFiles.eventHandler.listenToEvent('Files Updated', onDailyFilesUpdated)
|
||||
onDisplaceEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onDisplace', onDisplace)
|
||||
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
|
||||
/* Get ready for plotting. */
|
||||
recalculate()
|
||||
dimmensionsChangedEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('Dimmensions Changed', function () {
|
||||
recalculate()
|
||||
})
|
||||
|
||||
callBackFunction()
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) }
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
function initializeCoordinateSystem () {
|
||||
scaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
}
|
||||
|
||||
function finalizeCoordinateSystem () {
|
||||
coordinateSystem.eventHandler.stopListening(scaleChangedEventSubscriptionId)
|
||||
}
|
||||
|
||||
function onScaleChanged () {
|
||||
mustRecalculateDataPoints = true
|
||||
recalculate()
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
let userPosition = getDateFromPointAtBrowserCanvas(event, thisObject.container, coordinateSystem)
|
||||
userPositionDate = userPosition.valueOf()
|
||||
}
|
||||
|
||||
function onMarketFilesUpdated () {
|
||||
try {
|
||||
let newMarketFile = marketFiles.getFile(timeFrame)
|
||||
if (newMarketFile !== undefined) {
|
||||
marketFile = newMarketFile
|
||||
mustRecalculateDataPoints = true
|
||||
recalculate()
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onMarketFilesUpdated -> err = ' + err.stack.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onDailyFilesUpdated () {
|
||||
try {
|
||||
let newFileCursor = dailyFiles.getFileCursor(timeFrame)
|
||||
if (newFileCursor !== undefined) {
|
||||
fileCursor = newFileCursor
|
||||
mustRecalculateDataPoints = true
|
||||
recalculate()
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onDailyFilesUpdated -> err = ' + err.stack.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
try {
|
||||
let container
|
||||
/* First we check if this point is inside this space. */
|
||||
if (thisObject.container.frame.isThisPointHere(point) === true) {
|
||||
return thisObject.container
|
||||
} else {
|
||||
/* This point does not belong to this space. */
|
||||
return undefined
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] getContainer -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function setTimeFrame (pTimeFrame) {
|
||||
try {
|
||||
if (timeFrame !== pTimeFrame) {
|
||||
timeFrame = pTimeFrame
|
||||
mustRecalculateDataPoints = true
|
||||
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
|
||||
let newMarketFile = marketFiles.getFile(pTimeFrame)
|
||||
|
||||
if (newMarketFile !== undefined) {
|
||||
marketFile = newMarketFile
|
||||
recalculate()
|
||||
}
|
||||
} else {
|
||||
let newFileCursor = dailyFiles.getFileCursor(pTimeFrame)
|
||||
|
||||
if (newFileCursor !== undefined) {
|
||||
fileCursor = newFileCursor
|
||||
recalculate()
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] setTimeFrame -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime (pDatetime) {
|
||||
datetime = pDatetime
|
||||
}
|
||||
|
||||
function setCoordinateSystem (pCoordinateSystem) {
|
||||
finalizeCoordinateSystem()
|
||||
coordinateSystem = pCoordinateSystem
|
||||
initializeCoordinateSystem()
|
||||
}
|
||||
|
||||
function onDailyFileLoaded (event) {
|
||||
try {
|
||||
if (event.currentValue === event.totalValue) {
|
||||
/* This happens only when all of the files in the cursor have been loaded. */
|
||||
recalculate()
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onDailyFileLoaded -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
try {
|
||||
thisObject.container.frame.draw()
|
||||
plotChart()
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] draw -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculate () {
|
||||
try {
|
||||
if (timeFrame >= _1_HOUR_IN_MILISECONDS) {
|
||||
recalculateUsingMarketFiles()
|
||||
} else {
|
||||
recalculateUsingDailyFiles()
|
||||
}
|
||||
thisObject.container.eventHandler.raiseEvent('CandleStairs Changed', records) // --> Check This
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] recalculate -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function jsonifyDataFile (dataFile, recordDefinition, farLeftDate, farRightDate) {
|
||||
/*
|
||||
This function has as an input the raw data on files and creates with it an array of JSON objects
|
||||
with not calculated properties for later being consumed by Formulas
|
||||
*/
|
||||
|
||||
let jsonifiedArray = []
|
||||
let previous
|
||||
|
||||
for (let i = 0; i < dataFile.length; i++) {
|
||||
let record = {}
|
||||
for (let j = 0; j < recordDefinition.properties.length; j++) {
|
||||
let property = recordDefinition.properties[j]
|
||||
if (property.code.isCalculated !== true) {
|
||||
record[property.code.codeName] = dataFile[i][j]
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
(record.begin >= farLeftDate.valueOf() && record.end <= farRightDate.valueOf()) &&
|
||||
(record.end >= coordinateSystem.min.x && record.begin <= coordinateSystem.max.x)
|
||||
) {
|
||||
record.previous = previous
|
||||
jsonifiedArray.push(record)
|
||||
previous = record
|
||||
|
||||
if (datetime.valueOf() >= record.begin && datetime.valueOf() <= record.end) {
|
||||
thisObject.currentRecord = record
|
||||
thisObject.container.eventHandler.raiseEvent('Current Record Changed', thisObject.currentRecord)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return jsonifiedArray
|
||||
}
|
||||
|
||||
function calculationsProcedure (jsonArray, recordDefinition, calculationsProcedure, timeFrame) {
|
||||
/*
|
||||
This function has as an input an array of JSON objects, and it adds calculated properties to
|
||||
complete the set of properties that will be available.
|
||||
*/
|
||||
|
||||
let system = { // These are the available system variables to be used in User Code and Formulas
|
||||
timeFrame: timeFrame,
|
||||
ONE_DAY_IN_MILISECONDS: ONE_DAY_IN_MILISECONDS
|
||||
}
|
||||
let variable = {} // This is the structure where the user will define its own variables that will be shared across different code blocks and formulas.
|
||||
let results = []
|
||||
|
||||
/* This is Initialization Code */
|
||||
if (calculationsProcedure.initialization !== undefined) {
|
||||
if (calculationsProcedure.initialization.javascriptCode !== undefined) {
|
||||
try {
|
||||
eval(calculationsProcedure.initialization.javascriptCode.code)
|
||||
} catch (err) {
|
||||
logger.write('[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Error = ' + err.stack.stack)
|
||||
logger.write('[ERROR] calculationsProcedure -> initialization -> Error executing User Code. Code = ' + calculationsProcedure.initialization.javascriptCode.code)
|
||||
throw ('Error Executing User Code.')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* This is Initialization Code */
|
||||
if (calculationsProcedure.loop !== undefined) {
|
||||
if (calculationsProcedure.loop.javascriptCode !== undefined) {
|
||||
for (let index = 0; index < jsonArray.length; index++) {
|
||||
let product = jsonArray[index]
|
||||
|
||||
/* This is Loop Code */
|
||||
try {
|
||||
eval(calculationsProcedure.loop.javascriptCode.code)
|
||||
} catch (err) {
|
||||
logger.write('[ERROR] calculationsProcedure -> loop -> Error executing User Code. Error = ' + err.stack.stack)
|
||||
logger.write('[ERROR] calculationsProcedure -> loop -> Error executing User Code. product = ' + JSON.stringify(product))
|
||||
logger.write('[ERROR] calculationsProcedure -> loop -> Error executing User Code. Code = ' + calculationsProcedure.loop.javascriptCode.code)
|
||||
throw ('Error Executing User Code.')
|
||||
}
|
||||
|
||||
/* For each calculated property we apply its formula */
|
||||
for (let j = 0; j < recordDefinition.properties.length; j++) {
|
||||
let property = recordDefinition.properties[j]
|
||||
if (property.code.isCalculated === true) {
|
||||
if (property.formula !== undefined) {
|
||||
if (property.formula.code !== undefined) {
|
||||
try {
|
||||
let newValue = eval(property.formula.code)
|
||||
let currentRecord = product
|
||||
currentRecord[property.code.codeName] = newValue
|
||||
} catch (err) {
|
||||
logger.write('[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Error = ' + err.stack.stack)
|
||||
logger.write('[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. product = ' + JSON.stringify(product))
|
||||
logger.write('[ERROR] calculationsProcedure -> loop -> formula -> Error executing User Code. Code = ' + property.formula.code)
|
||||
throw ('Error Executing User Code.')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Adding the new element to the resulting array */
|
||||
results.push(product)
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
function recalculateUsingDailyFiles () {
|
||||
try {
|
||||
if (fileCursor === undefined) { return } // We need to wait until there is a fileCursor
|
||||
if (fileCursor.files.size === 0) { return } // We need to wait until there are files in the cursor
|
||||
|
||||
let daysOnSides = getSideDays(timeFrame)
|
||||
|
||||
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem)
|
||||
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem)
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf()
|
||||
|
||||
let farLeftDate = new Date(leftDate.valueOf() - dateDiff * 1.5)
|
||||
let farRightDate = new Date(rightDate.valueOf() + dateDiff * 1.5)
|
||||
|
||||
let currentDate = new Date(farLeftDate.valueOf())
|
||||
|
||||
records = []
|
||||
|
||||
while (currentDate.valueOf() <= farRightDate.valueOf() + ONE_DAY_IN_MILISECONDS) {
|
||||
let stringDate = currentDate.getFullYear() + '-' + pad(currentDate.getMonth() + 1, 2) + '-' + pad(currentDate.getDate(), 2)
|
||||
|
||||
let dailyFile = fileCursor.files.get(stringDate)
|
||||
|
||||
if (dailyFile !== undefined) {
|
||||
/* Transform the current file content into an array of JSON objects */
|
||||
let jsonData = jsonifyDataFile(dailyFile, productDefinition.record, farLeftDate, farRightDate)
|
||||
|
||||
/* Add the calculated properties */
|
||||
if (productDefinition.calculations !== undefined) {
|
||||
let calculationsResult = calculationsProcedure(jsonData, productDefinition.record, productDefinition.calculations, timeFrame)
|
||||
records.push(...calculationsResult) // This adds records to the current array.
|
||||
} else {
|
||||
records.push(...jsonData)// This adds records to the current array.
|
||||
}
|
||||
}
|
||||
|
||||
currentDate = new Date(currentDate.valueOf() + ONE_DAY_IN_MILISECONDS)
|
||||
}
|
||||
|
||||
/* Lests check if all the visible screen is going to be covered by candle-record. */
|
||||
let lowerEnd = leftDate.valueOf()
|
||||
let upperEnd = rightDate.valueOf()
|
||||
|
||||
if (records.length > 0) {
|
||||
if (records[0].begin > lowerEnd || records[records.length - 1].end < upperEnd) {
|
||||
setTimeout(recalculate, 2000)
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] recalculateUsingDailyFiles -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateUsingMarketFiles () {
|
||||
try {
|
||||
if (marketFile === undefined) { return } // Initialization not complete yet.
|
||||
|
||||
let daysOnSides = getSideDays(timeFrame)
|
||||
|
||||
let leftDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topLeft, thisObject.container, coordinateSystem)
|
||||
let rightDate = getDateFromPointAtBrowserCanvas(canvas.chartingSpace.viewport.visibleArea.topRight, thisObject.container, coordinateSystem)
|
||||
|
||||
let dateDiff = rightDate.valueOf() - leftDate.valueOf()
|
||||
|
||||
leftDate = new Date(leftDate.valueOf() - dateDiff * 1.5)
|
||||
rightDate = new Date(rightDate.valueOf() + dateDiff * 1.5)
|
||||
|
||||
for (let i = 0; i < records.length; i++) {
|
||||
let record = records[i]
|
||||
record.fillStyle = undefined
|
||||
record.strokeStyle = undefined
|
||||
record.dataPoints = undefined
|
||||
record.previous = undefined
|
||||
records[i] = {}
|
||||
}
|
||||
records = []
|
||||
|
||||
/* Transform the current file content into an array of JSON objects */
|
||||
let jsonData = jsonifyDataFile(marketFile, productDefinition.record, leftDate, rightDate)
|
||||
|
||||
/* Add the calculated properties */
|
||||
if (productDefinition.calculations !== undefined) {
|
||||
let calculationsResult = calculationsProcedure(jsonData, productDefinition.record, productDefinition.calculations, timeFrame)
|
||||
records.push(...calculationsResult) // This adds records to the current array.
|
||||
} else {
|
||||
records.push(...jsonData)// This adds records to the current array.
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] recalculateUsingMarketFiles -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function recalculateScale () {
|
||||
try {
|
||||
if (coordinateSystem.maxValue > 0) { return } // Already calculated.
|
||||
/* First we calculate the default scale */
|
||||
let minValue = {
|
||||
x: MIN_PLOTABLE_DATE.valueOf(),
|
||||
y: 0
|
||||
}
|
||||
|
||||
let maxValue = {
|
||||
x: MAX_PLOTABLE_DATE.valueOf(),
|
||||
y: nextPorwerOf10(MAX_DEFAULT_RATE_SCALE_VALUE) / 4 // TODO: This 4 is temporary
|
||||
}
|
||||
|
||||
coordinateSystem.initialize(
|
||||
minValue,
|
||||
maxValue,
|
||||
thisObject.container.frame.width,
|
||||
thisObject.container.frame.height
|
||||
)
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] recalculateScale -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function plotChart () {
|
||||
try {
|
||||
/* Clean the pannel at places where there is no record. */
|
||||
let currentRecord = {
|
||||
data: undefined
|
||||
}
|
||||
thisObject.container.eventHandler.raiseEvent('Current Record Changed', currentRecord)
|
||||
|
||||
for (let i = 0; i < records.length; i++) {
|
||||
let record = records[i]
|
||||
|
||||
if (i == 0) {
|
||||
record.previous = record
|
||||
} else {
|
||||
record.previous = records[i - 1]
|
||||
}
|
||||
|
||||
let beginPoint = {
|
||||
x: record.begin,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let endPoint = {
|
||||
x: record.end,
|
||||
y: 0
|
||||
}
|
||||
|
||||
beginPoint = coordinateSystem.transformThisPoint(beginPoint)
|
||||
endPoint = coordinateSystem.transformThisPoint(endPoint)
|
||||
|
||||
beginPoint = transformThisPoint(beginPoint, thisObject.container)
|
||||
endPoint = transformThisPoint(endPoint, thisObject.container)
|
||||
|
||||
beginPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(beginPoint)
|
||||
endPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(endPoint)
|
||||
|
||||
beginPoint = thisObject.fitFunction(beginPoint)
|
||||
endPoint = thisObject.fitFunction(endPoint)
|
||||
|
||||
if (endPoint.x < canvas.chartingSpace.viewport.visibleArea.bottomLeft.x || beginPoint.x > canvas.chartingSpace.viewport.visibleArea.bottomRight.x) {
|
||||
continue
|
||||
}
|
||||
|
||||
let atMousePosition = false
|
||||
if (userPositionDate >= record.begin && userPositionDate <= record.end) {
|
||||
atMousePosition = true
|
||||
|
||||
let currentRecord = {
|
||||
data: record
|
||||
}
|
||||
thisObject.container.eventHandler.raiseEvent('Current Record Changed', currentRecord)
|
||||
}
|
||||
|
||||
/* If there is code we execute it now. */
|
||||
if (productDefinition.referenceParent.javascriptCode !== undefined) {
|
||||
eval(productDefinition.referenceParent.javascriptCode.code)
|
||||
}
|
||||
|
||||
if (productDefinition.referenceParent.shapes === undefined) { continue }
|
||||
if (productDefinition.referenceParent.shapes.chartPoints === undefined) { continue }
|
||||
let dataPoints = new Map()
|
||||
if (record.dataPoints !== undefined && mustRecalculateDataPoints === false) {
|
||||
/* We use the datapoints already calculated. */
|
||||
dataPoints = record.dataPoints
|
||||
} else {
|
||||
if (logged === false) {
|
||||
logged = true
|
||||
}
|
||||
/* Only calculate the datapoints for this record, if we have not calculate it before. */
|
||||
for (let k = 0; k < productDefinition.referenceParent.shapes.chartPoints.length; k++) {
|
||||
let chartPoints = productDefinition.referenceParent.shapes.chartPoints[k]
|
||||
for (let j = 0; j < chartPoints.points.length; j++) {
|
||||
let point = chartPoints.points[j]
|
||||
if (point.pointFormula !== undefined) {
|
||||
let x = 0
|
||||
let y = 0
|
||||
eval(point.pointFormula.code)
|
||||
let dataPoint = {
|
||||
x: x,
|
||||
y: y
|
||||
}
|
||||
/* Contributing to Auto-Scale */
|
||||
coordinateSystem.reportYValue(dataPoint.y)
|
||||
|
||||
/*
|
||||
The information we store in files is independent from the charing system and its coordinate systems.
|
||||
That means that the first thing we allways need to do is to trasform these points to the coordinate system of the timeline.
|
||||
*/
|
||||
dataPoint = coordinateSystem.transformThisPoint(dataPoint)
|
||||
dataPoint = transformThisPoint(dataPoint, thisObject.container)
|
||||
dataPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(dataPoint)
|
||||
dataPoint = thisObject.fitFunction(dataPoint)
|
||||
|
||||
/* Store the data point at the local map */
|
||||
dataPoints.set(point.id, dataPoint)
|
||||
|
||||
if (plotterModuleConfig !== undefined) {
|
||||
if (plotterModuleConfig.slot !== undefined) {
|
||||
/* We reset the y coordinate since it will be transformed with another coordinate system to fit into a slot. */
|
||||
dataPoint.y = (-1) * y * coordinateSystem.scale.y + (plotterModuleConfig.slot.number - 1) * slotHeight + canvas.chartingSpace.viewport.visibleArea.topLeft.y
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Remember this datapoints, so that we do not need to calculate them again. */
|
||||
record.dataPoints = dataPoints
|
||||
}
|
||||
|
||||
for (let j = 0; j < productDefinition.referenceParent.shapes.polygons.length; j++) {
|
||||
let polygon = productDefinition.referenceParent.shapes.polygons[j]
|
||||
let calculatedStyle
|
||||
|
||||
/* Finding out the fill style */
|
||||
let fillStyle = { // default values
|
||||
opacity: 1,
|
||||
paletteColor: UI_COLOR.BLACK
|
||||
}
|
||||
if (polygon.polygonBody !== undefined) {
|
||||
if (record.fillStyle === undefined) {
|
||||
record.fillStyle = new Map()
|
||||
}
|
||||
calculatedStyle = record.fillStyle.get(polygon.id)
|
||||
if (calculatedStyle !== undefined) {
|
||||
/* We use the already calculated style */
|
||||
fillStyle = calculatedStyle
|
||||
} else {
|
||||
/* Get the default style if exists */
|
||||
if (polygon.polygonBody.style !== undefined) {
|
||||
if (polygon.polygonBody.style.code !== undefined) {
|
||||
if (polygon.polygonBody.style.code.default !== undefined) {
|
||||
let bodyStyle = polygon.polygonBody.style.code.default
|
||||
if (bodyStyle.opacity !== undefined) { fillStyle.opacity = bodyStyle.opacity }
|
||||
if (bodyStyle.paletteColor !== undefined) { fillStyle.paletteColor = eval(bodyStyle.paletteColor) }
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Apply conditional styles */
|
||||
if (polygon.polygonBody.styleConditions !== undefined) {
|
||||
for (let k = 0; k < polygon.polygonBody.styleConditions.length; k++) {
|
||||
let condition = polygon.polygonBody.styleConditions[k]
|
||||
let value = eval(condition.code)
|
||||
if (value === true) {
|
||||
if (condition.style !== undefined) {
|
||||
let bodyStyle = condition.style.code
|
||||
if (bodyStyle.opacity !== undefined) { fillStyle.opacity = bodyStyle.opacity }
|
||||
if (bodyStyle.paletteColor !== undefined) { fillStyle.paletteColor = eval(bodyStyle.paletteColor) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Get the atMousePosition style if exists */
|
||||
if (polygon.polygonBody.style !== undefined) {
|
||||
if (polygon.polygonBody.style.code.atMousePosition !== undefined) {
|
||||
let atMousePositionStyleDefinition = polygon.polygonBody.style.code.atMousePosition
|
||||
let atMousePositionStyle = {}
|
||||
if (atMousePositionStyleDefinition.opacity !== undefined) { atMousePositionStyle.opacity = atMousePositionStyleDefinition.opacity }
|
||||
if (atMousePositionStyleDefinition.paletteColor !== undefined) { atMousePositionStyle.paletteColor = eval(atMousePositionStyleDefinition.paletteColor) }
|
||||
atMousePositionFillStyles.set(polygon.id, atMousePositionStyle)
|
||||
}
|
||||
}
|
||||
|
||||
record.fillStyle.set(polygon.id, fillStyle)
|
||||
}
|
||||
}
|
||||
|
||||
/* Finding out the stroke style */
|
||||
let strokeStyle = { // default values
|
||||
opacity: 1,
|
||||
paletteColor: UI_COLOR.BLACK,
|
||||
lineWidth: 1,
|
||||
lineDash: [0, 0]
|
||||
}
|
||||
if (polygon.polygonBorder !== undefined) {
|
||||
if (record.strokeStyle === undefined) {
|
||||
record.strokeStyle = new Map()
|
||||
}
|
||||
calculatedStyle = record.strokeStyle.get(polygon.id)
|
||||
if (calculatedStyle !== undefined) {
|
||||
/* We use the already calculated style */
|
||||
strokeStyle = calculatedStyle
|
||||
} else {
|
||||
/* Get the default style if exists */
|
||||
if (polygon.polygonBorder.style !== undefined) {
|
||||
if (polygon.polygonBorder.style.code !== undefined) {
|
||||
if (polygon.polygonBorder.style.code.default !== undefined) {
|
||||
let borderStyle = polygon.polygonBorder.style.code.default
|
||||
if (borderStyle.opacity !== undefined) { strokeStyle.opacity = borderStyle.opacity }
|
||||
if (borderStyle.paletteColor !== undefined) { strokeStyle.paletteColor = eval(borderStyle.paletteColor) }
|
||||
if (borderStyle.lineWidth !== undefined) { strokeStyle.lineWidth = borderStyle.lineWidth }
|
||||
if (borderStyle.lineDash !== undefined) { strokeStyle.lineDash = borderStyle.lineDash }
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Apply conditional styles */
|
||||
if (polygon.polygonBorder.styleConditions !== undefined) {
|
||||
for (let k = 0; k < polygon.polygonBorder.styleConditions.length; k++) {
|
||||
let condition = polygon.polygonBorder.styleConditions[k]
|
||||
let value = eval(condition.code)
|
||||
if (value === true) {
|
||||
if (condition.style !== undefined) {
|
||||
let borderStyle = condition.style.code
|
||||
if (borderStyle.opacity !== undefined) { strokeStyle.opacity = borderStyle.opacity }
|
||||
if (borderStyle.paletteColor !== undefined) { strokeStyle.paletteColor = eval(borderStyle.paletteColor) }
|
||||
if (borderStyle.lineWidth !== undefined) { strokeStyle.lineWidth = borderStyle.lineWidth }
|
||||
if (borderStyle.lineDash !== undefined) { strokeStyle.lineDash = borderStyle.lineDash }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
/* Get the atMousePosition style if exists */
|
||||
if (polygon.polygonBorder.style !== undefined) {
|
||||
if (polygon.polygonBorder.style.code.atMousePosition !== undefined) {
|
||||
let atMousePositionStyleDefinition = polygon.polygonBorder.style.code.atMousePosition
|
||||
let atMousePositionStyle = {}
|
||||
if (atMousePositionStyleDefinition.opacity !== undefined) { atMousePositionStyle.opacity = atMousePositionStyleDefinition.opacity }
|
||||
if (atMousePositionStyleDefinition.paletteColor !== undefined) { atMousePositionStyle.paletteColor = eval(atMousePositionStyleDefinition.paletteColor) }
|
||||
if (atMousePositionStyleDefinition.lineWidth !== undefined) { atMousePositionStyle.lineWidth = atMousePositionStyleDefinition.lineWidth }
|
||||
if (atMousePositionStyleDefinition.lineDash !== undefined) { atMousePositionStyle.lineDash = atMousePositionStyleDefinition.lineDash }
|
||||
atMousePositionStrokeStyles.set(polygon.id, atMousePositionStyle)
|
||||
}
|
||||
}
|
||||
|
||||
record.strokeStyle.set(polygon.id, strokeStyle)
|
||||
}
|
||||
}
|
||||
|
||||
/* Draw the Polygon */
|
||||
browserCanvasContext.beginPath()
|
||||
for (let k = 0; k < polygon.polygonVertexes.length; k++) {
|
||||
let polygonVertex = polygon.polygonVertexes[k]
|
||||
if (polygonVertex.referenceParent !== undefined) {
|
||||
let dataPointObject = dataPoints.get(polygonVertex.referenceParent.id)
|
||||
let dataPoint = {
|
||||
x: dataPointObject.x,
|
||||
y: dataPointObject.y
|
||||
}
|
||||
/* We make sure the points do not fall outside the viewport visible area. This step allways need to be done. */
|
||||
dataPoint = canvas.chartingSpace.viewport.fitIntoVisibleArea(dataPoint)
|
||||
dataPoint = thisObject.fitFunction(dataPoint)
|
||||
if (k === 0) {
|
||||
browserCanvasContext.moveTo(dataPoint.x, dataPoint.y)
|
||||
} else {
|
||||
browserCanvasContext.lineTo(dataPoint.x, dataPoint.y)
|
||||
}
|
||||
}
|
||||
}
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
/* Apply the fill style to the canvas object */
|
||||
if (polygon.polygonBody !== undefined) {
|
||||
/* Replace the fill style if we are at mouse position */
|
||||
if (atMousePosition === true) {
|
||||
let atMousePositionFillStyle = atMousePositionFillStyles.get(polygon.id)
|
||||
if (atMousePositionFillStyle !== undefined) {
|
||||
fillStyle = atMousePositionFillStyle
|
||||
}
|
||||
}
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + fillStyle.paletteColor + ', ' + fillStyle.opacity + ')'
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
|
||||
/* Apply the stroke style to the canvas object */
|
||||
if (polygon.polygonBorder !== undefined) {
|
||||
/* Replace the stroke style if we are at mouse position */
|
||||
if (atMousePosition === true) {
|
||||
let atMousePositionStrokeStyle = atMousePositionStrokeStyles.get(polygon.id)
|
||||
if (atMousePositionStrokeStyle !== undefined) {
|
||||
strokeStyle = atMousePositionStrokeStyle
|
||||
}
|
||||
}
|
||||
|
||||
browserCanvasContext.lineWidth = strokeStyle.lineWidth
|
||||
browserCanvasContext.setLineDash(strokeStyle.lineDash)
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + strokeStyle.paletteColor + ', ' + strokeStyle.opacity + ')'
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (coordinateSystem.autoMinYScale === true || coordinateSystem.autosMaxScale === true) {
|
||||
mustRecalculateDataPoints = true
|
||||
} else {
|
||||
mustRecalculateDataPoints = false
|
||||
}
|
||||
|
||||
logged = false
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] plotChart -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onViewportZoomChanged (event) {
|
||||
try {
|
||||
mustRecalculateDataPoints = true
|
||||
recalculate()
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onViewportZoomChanged -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onDragFinished () {
|
||||
try {
|
||||
recalculate()
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] onDragFinished -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function onDisplace (event) {
|
||||
recalculateAll(event)
|
||||
}
|
||||
|
||||
function onViewportPositionChanged (event) {
|
||||
recalculateAll(event)
|
||||
}
|
||||
|
||||
function recalculateAll (event) {
|
||||
mustRecalculateDataPoints = true
|
||||
if (event !== undefined) {
|
||||
if (event.recalculate === true) {
|
||||
recalculate()
|
||||
return
|
||||
}
|
||||
}
|
||||
if (Math.random() * 100 > 95) {
|
||||
recalculate()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,82 @@
|
||||
let canvas
|
||||
let markets
|
||||
let ecosystem = newEcosystem()
|
||||
let systemEventHandler
|
||||
|
||||
let APP_SCHEMA_MAP = new Map()
|
||||
let APP_SCHEMA_ARRAY = []
|
||||
|
||||
function newDashboard () {
|
||||
const MODULE_NAME = 'Dashboard'
|
||||
const ERROR_LOG = true
|
||||
const INTENSIVE_LOG = false
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
start: start
|
||||
}
|
||||
|
||||
const DEBUG_START_UP_DELAY = 0 // 3000; // This is a waiting time in case there is a need to debug the very first steps of initialization, to be able to hit F12 on time.
|
||||
|
||||
return thisObject
|
||||
|
||||
function start () {
|
||||
try {
|
||||
setBrowserEvents()
|
||||
|
||||
systemEventHandler = newSystemEventHandler()
|
||||
systemEventHandler.initialize(setUpAppSchema)
|
||||
|
||||
function setUpAppSchema () {
|
||||
APP_SCHEMA_ARRAY = getAppSchema()
|
||||
for (let i = 0; i < APP_SCHEMA_ARRAY.length; i++) {
|
||||
let nodeDefinition = APP_SCHEMA_ARRAY[i]
|
||||
let key = nodeDefinition.type
|
||||
|
||||
APP_SCHEMA_MAP.set(key, nodeDefinition)
|
||||
}
|
||||
|
||||
startCanvas()
|
||||
}
|
||||
|
||||
function startCanvas () {
|
||||
/* If this method is executed for a second time, it should finalize the current execution structure */
|
||||
|
||||
if (canvas !== undefined) { canvas.finalize() }
|
||||
|
||||
setTimeout(delayedStart, DEBUG_START_UP_DELAY)
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] start -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function delayedStart () {
|
||||
try {
|
||||
/* For now, we are supporting only one market. */
|
||||
|
||||
let market = {
|
||||
id: 2,
|
||||
baseAsset: 'USDT',
|
||||
quotedAsset: 'BTC'
|
||||
}
|
||||
|
||||
markets = new Map()
|
||||
|
||||
markets.set(market.id, market)
|
||||
|
||||
canvas = newCanvas()
|
||||
canvas.initialize()
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] delayedStart -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function setBrowserEvents () {
|
||||
window.onbeforeunload = onBrowserClosed
|
||||
function onBrowserClosed () {
|
||||
// canvas.designSpace.workspace.stopAllRunningTasks()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,333 @@
|
||||
|
||||
function newProductStorage (name) {
|
||||
const MODULE_NAME = 'Product Storage'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
/*
|
||||
|
||||
This object will initialize children objects that will end up loading the data of each set defined at each product of the bot received at initialization.
|
||||
Once all the underlaying objects are fully initialized it will callback.
|
||||
|
||||
At the same time it will raise an event for each underlaying file being loaded, so that the UI can reflect the progress to the end user.
|
||||
|
||||
Product Storage
|
||||
|
|
||||
|-----> SingleFile
|
||||
|
|
||||
|-----> FileSequence
|
||||
|
|
||||
|-----> DailyFiles -----> FileCursor
|
||||
|
|
||||
|-----> MarketFiles
|
||||
|
||||
*/
|
||||
|
||||
let thisObject = {
|
||||
eventHandler: undefined,
|
||||
marketFiles: [],
|
||||
dailyFiles: [],
|
||||
singleFile: [],
|
||||
fileSequences: [],
|
||||
setDatetime: setDatetime,
|
||||
setTimeFrame: setTimeFrame,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.eventHandler = newEventHandler()
|
||||
|
||||
/* We name the event Handler to easy debugging. */
|
||||
|
||||
thisObject.eventHandler.name = 'Storage-' + name
|
||||
|
||||
let datetime
|
||||
let timeFrame
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
try {
|
||||
for (let i = 0; i < thisObject.marketFiles.length; i++) {
|
||||
let marketFile = thisObject.marketFiles[i]
|
||||
marketFile.finalize()
|
||||
}
|
||||
|
||||
thisObject.marketFiles = undefined
|
||||
|
||||
for (let i = 0; i < thisObject.dailyFiles.length; i++) {
|
||||
let dailyFile = thisObject.dailyFiles[i]
|
||||
dailyFile.finalize()
|
||||
}
|
||||
|
||||
thisObject.dailyFiles = undefined
|
||||
|
||||
for (let i = 0; i < thisObject.fileSequences.length; i++) {
|
||||
let fileSequence = thisObject.fileSequences[i]
|
||||
fileSequence.finalize()
|
||||
}
|
||||
|
||||
thisObject.fileSequences = undefined
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] finalize -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
|
||||
function initialize (dataMine, bot, session, product, exchange, market, pDatetime, pTimeFrame, callBackFunction) {
|
||||
try {
|
||||
datetime = pDatetime
|
||||
timeFrame = pTimeFrame
|
||||
|
||||
let dataSetsToLoad = 0
|
||||
let dataSetsLoaded = 0
|
||||
|
||||
for (let i = 0; i < product.datasets.length; i++) {
|
||||
let dataset = product.datasets[i]
|
||||
|
||||
switch (dataset.code.type) {
|
||||
case 'Market Files': {
|
||||
dataSetsToLoad++
|
||||
|
||||
let marketFiles = newMarketFiles()
|
||||
marketFiles.initialize(dataMine, bot, session, product, dataset, exchange, market, onMarketFileReady)
|
||||
thisObject.marketFiles.push(marketFiles)
|
||||
}
|
||||
break
|
||||
|
||||
case 'Daily Files': {
|
||||
dataSetsToLoad++
|
||||
|
||||
let dailyFiles = newDailyFiles()
|
||||
dailyFiles.initialize(dataMine, bot, session, product, dataset, exchange, market, pDatetime, pTimeFrame, onDailyFileReady)
|
||||
thisObject.dailyFiles.push(dailyFiles)
|
||||
}
|
||||
break
|
||||
|
||||
case 'Single File': {
|
||||
dataSetsToLoad++
|
||||
|
||||
let singleFile = newSingleFile()
|
||||
singleFile.initialize(dataMine, bot, session, product, dataset, exchange, market, onSingleFileReady)
|
||||
thisObject.singleFile.push(singleFile)
|
||||
}
|
||||
break
|
||||
|
||||
case 'File Sequence': {
|
||||
dataSetsToLoad++
|
||||
|
||||
let fileSequences = newFileSequence()
|
||||
fileSequences.initialize(dataMine, bot, session, product, dataset, exchange, market, onFileSequenceReady)
|
||||
thisObject.fileSequences.push(fileSequences)
|
||||
}
|
||||
break
|
||||
default:
|
||||
if (ERROR_LOG === true) { logger.write('[WARN] initialize -> dataset with no type defined or type not supported -> dataset.code.type = ' + dataset.code.type) }
|
||||
}
|
||||
|
||||
function onMarketFileReady (err, pCaller) {
|
||||
try {
|
||||
switch (err.result) {
|
||||
case GLOBAL.DEFAULT_OK_RESPONSE.result: {
|
||||
break
|
||||
}
|
||||
case GLOBAL.DEFAULT_FAIL_RESPONSE.result: {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
}
|
||||
let event = {
|
||||
totalValue: pCaller.getExpectedFiles(),
|
||||
currentValue: pCaller.getFilesLoaded(),
|
||||
filesNotLoaded: pCaller.getFilesNotLoaded()
|
||||
}
|
||||
|
||||
thisObject.eventHandler.raiseEvent('Market File Loaded', event)
|
||||
|
||||
if (event.filesNotLoaded === event.totalValue) {
|
||||
dataSetsToLoad--
|
||||
checkInitializeComplete()
|
||||
return
|
||||
}
|
||||
|
||||
if (event.currentValue + event.filesNotLoaded === event.totalValue) {
|
||||
dataSetsLoaded++
|
||||
checkInitializeComplete()
|
||||
}
|
||||
} catch (err) {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
function onDailyFileReady (err, pCaller) {
|
||||
try {
|
||||
switch (err.result) {
|
||||
case GLOBAL.DEFAULT_OK_RESPONSE.result: {
|
||||
break
|
||||
}
|
||||
|
||||
case GLOBAL.DEFAULT_FAIL_RESPONSE.result: {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
case GLOBAL.CUSTOM_FAIL_RESPONSE.result: {
|
||||
if (err.message === 'Dataset Unavailable.') {
|
||||
dataSetsToLoad--
|
||||
checkInitializeComplete()
|
||||
return
|
||||
} else {
|
||||
callBackFunction(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
default: {
|
||||
callBackFunction(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
let event = {
|
||||
totalValue: pCaller.getExpectedFiles(),
|
||||
currentValue: pCaller.getFilesLoaded()
|
||||
}
|
||||
|
||||
thisObject.eventHandler.raiseEvent('Daily File Loaded', event)
|
||||
|
||||
if (event.currentValue === event.totalValue) {
|
||||
dataSetsLoaded++
|
||||
checkInitializeComplete()
|
||||
}
|
||||
} catch (err) {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
function onSingleFileReady (err, pCaller) {
|
||||
try {
|
||||
switch (err.result) {
|
||||
case GLOBAL.DEFAULT_OK_RESPONSE.result: {
|
||||
break
|
||||
}
|
||||
|
||||
case GLOBAL.DEFAULT_FAIL_RESPONSE.result: {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
|
||||
case GLOBAL.CUSTOM_FAIL_RESPONSE.result: {
|
||||
callBackFunction(err)
|
||||
return
|
||||
}
|
||||
|
||||
default: {
|
||||
callBackFunction(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
let event = {
|
||||
totalValue: 1,
|
||||
currentValue: 1
|
||||
}
|
||||
thisObject.eventHandler.raiseEvent('Single File Loaded', event)
|
||||
|
||||
if (event.currentValue === event.totalValue) {
|
||||
dataSetsLoaded++
|
||||
checkInitializeComplete()
|
||||
}
|
||||
} catch (err) {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
function onFileSequenceReady (err, pCaller) {
|
||||
try {
|
||||
switch (err.result) {
|
||||
case GLOBAL.DEFAULT_OK_RESPONSE.result: {
|
||||
break
|
||||
}
|
||||
case GLOBAL.DEFAULT_FAIL_RESPONSE.result: {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
return
|
||||
}
|
||||
case GLOBAL.CUSTOM_FAIL_RESPONSE.result: {
|
||||
callBackFunction(err)
|
||||
return
|
||||
}
|
||||
default: {
|
||||
callBackFunction(err)
|
||||
return
|
||||
}
|
||||
}
|
||||
let event
|
||||
if (pCaller !== undefined) {
|
||||
event = {
|
||||
totalValue: pCaller.getExpectedFiles(),
|
||||
currentValue: pCaller.getFilesLoaded()
|
||||
}
|
||||
} else { // When there are no data the reference to the caller wont arrive here.
|
||||
event = {
|
||||
totalValue: 1,
|
||||
currentValue: 1
|
||||
}
|
||||
}
|
||||
|
||||
thisObject.eventHandler.raiseEvent('File Sequence Loaded', event)
|
||||
|
||||
if (event.currentValue === event.totalValue) {
|
||||
dataSetsLoaded++
|
||||
checkInitializeComplete()
|
||||
}
|
||||
} catch (err) {
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
function checkInitializeComplete () {
|
||||
try {
|
||||
if (dataSetsLoaded === dataSetsToLoad) {
|
||||
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE)
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> checkInitializeComplete -> err = ' + err.stack) }
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] initialize -> err = ' + err.stack) }
|
||||
callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE)
|
||||
}
|
||||
}
|
||||
|
||||
function setDatetime (pDatetime) {
|
||||
/* 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)
|
||||
let newDate = Math.trunc(pDatetime.valueOf() / ONE_DAY_IN_MILISECONDS)
|
||||
|
||||
datetime = new Date(pDatetime)
|
||||
|
||||
if (currentDate !== newDate) {
|
||||
if (timeFrame <= _1_HOUR_IN_MILISECONDS) {
|
||||
for (let i = 0; i < thisObject.dailyFiles.length; i++) {
|
||||
thisObject.dailyFiles[i].setDatetime(pDatetime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setTimeFrame (pTimeFrame) {
|
||||
/* We are going to filter out the cases in which the timeFrame received is the same that the one we already know. */
|
||||
|
||||
if (timeFrame !== pTimeFrame) {
|
||||
timeFrame = pTimeFrame
|
||||
|
||||
if (timeFrame <= _1_HOUR_IN_MILISECONDS) {
|
||||
for (let i = 0; i < thisObject.dailyFiles.length; i++) {
|
||||
thisObject.dailyFiles[i].setTimeFrame(pTimeFrame, datetime)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,194 @@
|
||||
function newAutoScaleButton () {
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
parentContainer: undefined,
|
||||
autoMinScale: undefined,
|
||||
autoMaxScale: undefined,
|
||||
setStatus: setStatus,
|
||||
draw: draw,
|
||||
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
/* Cointainer stuff */
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.name = 'Auto Scale Button'
|
||||
thisObject.container.initialize()
|
||||
thisObject.container.isClickeable = true
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.frame.containerName = 'Auto Scale Button'
|
||||
|
||||
const ICON_SIZE = 36
|
||||
let onMouseWheelEventSubscriptionId
|
||||
let coordinateSystem
|
||||
let axis
|
||||
let currentStatus
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(onMouseWheelEventSubscriptionId)
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.parentContainer = undefined
|
||||
thisObject.panels = undefined
|
||||
|
||||
coordinateSystem = undefined
|
||||
axis = undefined
|
||||
}
|
||||
|
||||
function initialize (pAxis, pCoordinateSystem) {
|
||||
axis = pAxis
|
||||
coordinateSystem = pCoordinateSystem
|
||||
thisObject.autoMinScale = coordinateSystem.autoMinYScale
|
||||
thisObject.autoMaxScale = coordinateSystem.autoMaxYScale
|
||||
|
||||
switch (axis) {
|
||||
case 'X': {
|
||||
thisObject.container.frame.width = ICON_SIZE
|
||||
thisObject.container.frame.height = ICON_SIZE
|
||||
|
||||
let position = {
|
||||
x: thisObject.container.parentContainer.frame.width * 7 / 8,
|
||||
y: thisObject.container.parentContainer.frame.height / 2
|
||||
}
|
||||
|
||||
thisObject.container.frame.position = position
|
||||
break
|
||||
}
|
||||
case 'Y': {
|
||||
thisObject.container.frame.width = ICON_SIZE
|
||||
thisObject.container.frame.height = ICON_SIZE
|
||||
|
||||
let position = {
|
||||
x: thisObject.container.parentContainer.frame.width * 7 / 8,
|
||||
y: thisObject.container.parentContainer.frame.height / 2
|
||||
}
|
||||
|
||||
thisObject.container.frame.position = position
|
||||
break
|
||||
}
|
||||
}
|
||||
onMouseWheelEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel)
|
||||
}
|
||||
|
||||
function setStatus (autoMinScale, autoMaxScale) {
|
||||
thisObject.autoMinScale = autoMinScale
|
||||
thisObject.autoMaxScale = autoMaxScale
|
||||
|
||||
if (thisObject.autoMinScale === false && thisObject.autoMaxScale === false) {
|
||||
currentStatus = 1
|
||||
}
|
||||
if (thisObject.autoMinScale === true && thisObject.autoMaxScale === false) {
|
||||
currentStatus = 2
|
||||
}
|
||||
if (thisObject.autoMinScale === false && thisObject.autoMaxScale === true) {
|
||||
currentStatus = 3
|
||||
}
|
||||
if (thisObject.autoMinScale === true && thisObject.autoMaxScale === true) {
|
||||
currentStatus = 4
|
||||
}
|
||||
}
|
||||
function onMouseWheel (event) {
|
||||
let factor
|
||||
let delta = event.wheelDelta
|
||||
if (delta < 0) {
|
||||
delta = -1
|
||||
} else {
|
||||
delta = 1
|
||||
}
|
||||
currentStatus = currentStatus + delta
|
||||
if (currentStatus === 5) { currentStatus = 1 }
|
||||
if (currentStatus === 0) { currentStatus = 4 }
|
||||
|
||||
switch (currentStatus) {
|
||||
case 1: {
|
||||
thisObject.autoMinScale = false
|
||||
thisObject.autoMaxScale = false
|
||||
update()
|
||||
return
|
||||
}
|
||||
case 2: {
|
||||
thisObject.autoMinScale = true
|
||||
thisObject.autoMaxScale = false
|
||||
update()
|
||||
return
|
||||
}
|
||||
case 3: {
|
||||
thisObject.autoMinScale = false
|
||||
thisObject.autoMaxScale = true
|
||||
update()
|
||||
return
|
||||
}
|
||||
case 4: {
|
||||
thisObject.autoMinScale = true
|
||||
thisObject.autoMaxScale = true
|
||||
update()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function update () {
|
||||
switch (axis) {
|
||||
case 'X': {
|
||||
coordinateSystem.autoMinXScale = thisObject.autoMinScale
|
||||
coordinateSystem.autoMaxXScale = thisObject.autoMaxScale
|
||||
break
|
||||
}
|
||||
case 'Y': {
|
||||
coordinateSystem.autoMinYScale = thisObject.autoMinScale
|
||||
coordinateSystem.autoMaxYScale = thisObject.autoMaxScale
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
coordinateSystem.recalculateScale()
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point, purpose) {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
let icon
|
||||
switch (axis) {
|
||||
case 'X': {
|
||||
if (thisObject.autoMinScale === false && thisObject.autoMaxScale === false) {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-auto-time-scale-manual')
|
||||
}
|
||||
if (thisObject.autoMinScale === true && thisObject.autoMaxScale === false) {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-auto-time-scale-auto-min')
|
||||
}
|
||||
if (thisObject.autoMinScale === false && thisObject.autoMaxScale === true) {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-auto-time-scale-auto-max')
|
||||
}
|
||||
if (thisObject.autoMinScale === true && thisObject.autoMaxScale === true) {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-auto-time-scale-auto-min-max')
|
||||
}
|
||||
drawIcon(icon, 0, 0, 0, 0, ICON_SIZE, thisObject.container)
|
||||
break
|
||||
}
|
||||
case 'Y': {
|
||||
if (thisObject.autoMinScale === false && thisObject.autoMaxScale === false) {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-auto-scale-manual')
|
||||
}
|
||||
if (thisObject.autoMinScale === true && thisObject.autoMaxScale === false) {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-auto-scale-auto-min')
|
||||
}
|
||||
if (thisObject.autoMinScale === false && thisObject.autoMaxScale === true) {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-auto-scale-auto-max')
|
||||
}
|
||||
if (thisObject.autoMinScale === true && thisObject.autoMaxScale === true) {
|
||||
icon = canvas.designSpace.iconCollection.get('toggle-auto-scale-auto-min-max')
|
||||
}
|
||||
drawIcon(icon, 0, 0, 0, 0, ICON_SIZE, thisObject.container)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
|
||||
function drawScaleDisplay (label1, label2, label3, xExtraOffSet1, xExtraOffSet2, xExtraOffSet3, icon1, icon2, container, backgroundColor) {
|
||||
const RED_LINE_HIGHT = 4
|
||||
const OPACITY = 1
|
||||
|
||||
let params = {
|
||||
cornerRadius: 5,
|
||||
lineWidth: 1,
|
||||
container: container,
|
||||
borderColor: UI_COLOR.RUSTED_RED,
|
||||
castShadow: false,
|
||||
backgroundColor: backgroundColor,
|
||||
opacity: OPACITY
|
||||
}
|
||||
|
||||
roundedCornersBackground(params)
|
||||
|
||||
/* Place the Text */
|
||||
|
||||
label1 = label1.substring(0, 18)
|
||||
label2 = label2.substring(0, 20)
|
||||
label3 = label3.substring(0, 20)
|
||||
|
||||
drawLabel(label1, 1 / 2, 92 / 100, 0, 0, 9, container)
|
||||
drawLabel(label2, 1 / 2, 42 / 100, 0, 0, 17, container)
|
||||
drawLabel(label3, 1 / 2, 67 / 100, 0, 0, 9, container)
|
||||
|
||||
drawIcon(icon1, 1 / 8, 1 / 2, 0, 0, 28, container)
|
||||
drawIcon(icon2, 7 / 8, 1 / 2, 0, 0, 28, container)
|
||||
}
|
||||
|
||||
function drawScaleDisplayCover (container) {
|
||||
let params = {
|
||||
cornerRadius: 5,
|
||||
lineWidth: 1,
|
||||
container: container,
|
||||
borderColor: UI_COLOR.WHITE,
|
||||
castShadow: false,
|
||||
backgroundColor: UI_COLOR.WHITE,
|
||||
opacity: '0.25'
|
||||
}
|
||||
|
||||
roundedCornersBackground(params)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,423 @@
|
||||
function newRateScale () {
|
||||
const MODULE_NAME = 'Right Scale'
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
rate: undefined,
|
||||
fitFunction: undefined,
|
||||
payload: undefined,
|
||||
minValue: undefined,
|
||||
maxValue: undefined,
|
||||
isVisible: true,
|
||||
layersOn: undefined,
|
||||
onUpstreamScaleChanged: onUpstreamScaleChanged,
|
||||
onMouseOverSomeTimeMachineContainer: onMouseOverSomeTimeMachineContainer,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isWheelable = true
|
||||
thisObject.container.detectMouseOver = true
|
||||
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL
|
||||
thisObject.container.frame.height = 40
|
||||
|
||||
let draggeableContainer = newContainer()
|
||||
draggeableContainer.initialize(MODULE_NAME + ' Draggeable')
|
||||
draggeableContainer.isDraggeable = true
|
||||
|
||||
let visible = true
|
||||
let isMouseOver
|
||||
|
||||
let onMouseWheelEventSubscriptionId
|
||||
let onMouseOverEventSubscriptionId
|
||||
let onMouseNotOverEventSubscriptionId
|
||||
let onScaleChangedEventSubscriptionId
|
||||
|
||||
let coordinateSystem
|
||||
let limitingContainer
|
||||
let rateCalculationsContainer
|
||||
|
||||
let mouse = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
|
||||
let scaleDisplayTimer = 0
|
||||
let autoScaleButton
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(onMouseWheelEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId)
|
||||
coordinateSystem.eventHandler.stopListening(onScaleChangedEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.payload = undefined
|
||||
|
||||
coordinateSystem = undefined
|
||||
limitingContainer = undefined
|
||||
rateCalculationsContainer = undefined
|
||||
mouse = undefined
|
||||
|
||||
autoScaleButton.finalize()
|
||||
autoScaleButton = undefined
|
||||
}
|
||||
|
||||
function initialize (pCoordinateSystem, pLimitingContainer, pRateCalculationsContainer) {
|
||||
coordinateSystem = pCoordinateSystem
|
||||
limitingContainer = pLimitingContainer
|
||||
rateCalculationsContainer = pRateCalculationsContainer
|
||||
|
||||
thisObject.minValue = coordinateSystem.min.y
|
||||
thisObject.maxValue = coordinateSystem.max.y
|
||||
|
||||
onMouseWheelEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel)
|
||||
onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
||||
onScaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
|
||||
autoScaleButton = newAutoScaleButton()
|
||||
autoScaleButton.container.connectToParent(thisObject.container)
|
||||
autoScaleButton.initialize('Y', coordinateSystem)
|
||||
|
||||
readObjectState()
|
||||
}
|
||||
|
||||
function onMouseOverSomeTimeMachineContainer (event) {
|
||||
if (event.containerId === undefined) {
|
||||
/* This happens when the mouse over was not at the instance of a certain scale, but anywhere else. */
|
||||
visible = true
|
||||
} else {
|
||||
if (event.containerId === thisObject.container.id) {
|
||||
visible = true
|
||||
} else {
|
||||
visible = false
|
||||
turnOnCounter = 0
|
||||
}
|
||||
}
|
||||
mouse = {
|
||||
position: {
|
||||
x: event.x,
|
||||
y: event.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onScaleChanged () {
|
||||
saveObjectState()
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
isMouseOver = true
|
||||
event.containerId = thisObject.container.id
|
||||
thisObject.container.eventHandler.raiseEvent('onMouseOverScale', event)
|
||||
}
|
||||
|
||||
function onMouseNotOver () {
|
||||
isMouseOver = false
|
||||
scaleDisplayTimer = 0
|
||||
}
|
||||
|
||||
function onMouseWheel (event) {
|
||||
if (event.shiftKey === true) {
|
||||
autoScaleButton.container.eventHandler.raiseEvent('onMouseWheel', event)
|
||||
return
|
||||
}
|
||||
|
||||
let factor
|
||||
let morePower = 10
|
||||
if (event.buttons === 4) { morePower = 1 } // Mouse wheel pressed.
|
||||
|
||||
let delta = event.wheelDelta
|
||||
if (delta < 0) {
|
||||
factor = -0.01 * morePower
|
||||
} else {
|
||||
factor = 0.01 * morePower
|
||||
}
|
||||
|
||||
coordinateSystem.zoomY(factor, event, limitingContainer)
|
||||
scaleDisplayTimer = 100
|
||||
}
|
||||
|
||||
function getContainer (point, purpose) {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
if (purpose === GET_CONTAINER_PURPOSE.DRAGGING) {
|
||||
return draggeableContainer
|
||||
}
|
||||
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
|
||||
function saveObjectState () {
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
code.minValue = coordinateSystem.min.y
|
||||
code.maxValue = coordinateSystem.max.y
|
||||
code.autoMinScale = coordinateSystem.autoMinYScale
|
||||
code.autoMaxScale = coordinateSystem.autoMaxYScale
|
||||
thisObject.payload.node.code = JSON.stringify(code, null, 4)
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function readObjectState () {
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
|
||||
if (
|
||||
(isNaN(code.minValue) || code.minValue === null || code.minValue === undefined) ||
|
||||
(isNaN(code.maxValue) || code.maxValue === null || code.maxValue === undefined)
|
||||
) {
|
||||
// not using this value
|
||||
} else {
|
||||
if (
|
||||
thisObject.minValue !== code.minValue ||
|
||||
thisObject.maxValue !== code.maxValue ||
|
||||
coordinateSystem.autoMinYScale !== code.autoMinScale ||
|
||||
coordinateSystem.autoMaxYScale !== code.autoMaxScale
|
||||
) {
|
||||
thisObject.minValue = code.minValue
|
||||
thisObject.maxValue = code.maxValue
|
||||
coordinateSystem.min.y = thisObject.minValue
|
||||
coordinateSystem.max.y = thisObject.maxValue
|
||||
|
||||
if (code.autoMinScale !== undefined && (code.autoMinScale === true || code.autoMinScale === false) && code.autoMaxScale !== undefined && (code.autoMaxScale === true || code.autoMaxScale === false)) {
|
||||
coordinateSystem.autoMinYScale = code.autoMinScale
|
||||
coordinateSystem.autoMaxYScale = code.autoMaxScale
|
||||
autoScaleButton.setStatus(code.autoMinScale, code.autoMaxScale)
|
||||
}
|
||||
|
||||
coordinateSystem.recalculateScale()
|
||||
}
|
||||
}
|
||||
saveObjectState() // this overrides any invalid value at the config.
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
scaleDisplayTimer--
|
||||
readObjectState()
|
||||
positioningPhysics()
|
||||
draggingPhysics()
|
||||
}
|
||||
|
||||
function draggingPhysics () {
|
||||
if (draggeableContainer.frame.position.y === 0) { return }
|
||||
|
||||
let dragVector = {
|
||||
x: draggeableContainer.frame.position.x,
|
||||
y: draggeableContainer.frame.position.y
|
||||
}
|
||||
|
||||
draggeableContainer.frame.position.x = 0
|
||||
draggeableContainer.frame.position.y = 0
|
||||
|
||||
let point = {
|
||||
x: 0,
|
||||
y: -dragVector.y
|
||||
}
|
||||
|
||||
let newMaxRate = getRateFromPointAtContainer(point, limitingContainer, coordinateSystem)
|
||||
let yDifferenceMaxMin = coordinateSystem.max.y - coordinateSystem.min.y
|
||||
|
||||
coordinateSystem.min.y = newMaxRate - yDifferenceMaxMin
|
||||
coordinateSystem.max.y = newMaxRate
|
||||
coordinateSystem.recalculateScale()
|
||||
}
|
||||
|
||||
function onUpstreamScaleChanged (event) {
|
||||
if (event === undefined) { return }
|
||||
if (event.type === 'center dragged') {
|
||||
let point = {
|
||||
x: event.dragVector.x,
|
||||
y: -event.dragVector.y
|
||||
}
|
||||
let newMinDate = getDateFromPointAtContainer(point, rateCalculationsContainer, coordinateSystem)
|
||||
let newMaxRate = getRateFromPointAtContainer(point, rateCalculationsContainer, coordinateSystem)
|
||||
let xDifferenceMaxMin = coordinateSystem.max.x - coordinateSystem.min.x
|
||||
let yDifferenceMaxMin = coordinateSystem.max.y - coordinateSystem.min.y
|
||||
coordinateSystem.min.x = newMinDate.valueOf()
|
||||
coordinateSystem.max.x = newMinDate.valueOf() + xDifferenceMaxMin
|
||||
coordinateSystem.min.y = newMaxRate - yDifferenceMaxMin
|
||||
coordinateSystem.max.y = newMaxRate
|
||||
|
||||
coordinateSystem.recalculateScale()
|
||||
}
|
||||
if (event.type === 'top dragged') {
|
||||
let point = {
|
||||
x: event.dragVector.x,
|
||||
y: event.dragVector.y
|
||||
}
|
||||
let newMaxRate = getRateFromPointAtContainer(point, rateCalculationsContainer, coordinateSystem)
|
||||
|
||||
coordinateSystem.max.y = newMaxRate
|
||||
coordinateSystem.maxHeight = rateCalculationsContainer.frame.height
|
||||
coordinateSystem.recalculateScale()
|
||||
}
|
||||
if (event.type === 'bottom dragged') {
|
||||
let point = {
|
||||
x: event.dragVector.x,
|
||||
y: event.dragVector.y + rateCalculationsContainer.frame.height
|
||||
}
|
||||
let newMinRate = getRateFromPointAtContainer(point, rateCalculationsContainer, coordinateSystem)
|
||||
|
||||
coordinateSystem.min.y = newMinRate
|
||||
coordinateSystem.maxHeight = rateCalculationsContainer.frame.height
|
||||
coordinateSystem.recalculateScale()
|
||||
}
|
||||
}
|
||||
|
||||
function positioningPhysics () {
|
||||
/* Container Limits */
|
||||
|
||||
let upCorner = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let bottonCorner = {
|
||||
x: limitingContainer.frame.width,
|
||||
y: limitingContainer.frame.height
|
||||
}
|
||||
|
||||
upCorner = transformThisPoint(upCorner, limitingContainer)
|
||||
bottonCorner = transformThisPoint(bottonCorner, limitingContainer)
|
||||
|
||||
upCorner = limitingContainer.fitFunction(upCorner, true)
|
||||
bottonCorner = limitingContainer.fitFunction(bottonCorner, true)
|
||||
|
||||
/* We will check if we need to change the bottom because of being one of many scales of the same type */
|
||||
let displaceFactor = 0
|
||||
if (thisObject.payload.parentNode === undefined) { return } // This happens when in the process of deleting the scale, timeline chart or time machine.
|
||||
if (thisObject.payload.parentNode.type === 'Timeline Chart') {
|
||||
if (thisObject.payload.parentNode.payload.parentNode !== undefined) {
|
||||
if (thisObject.payload.parentNode.payload.parentNode.rateScale !== undefined) {
|
||||
if (thisObject.payload.parentNode.payload.parentNode.rateScale.payload.isVisible === true) {
|
||||
displaceFactor++
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < thisObject.payload.parentNode.payload.parentNode.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.payload.parentNode.payload.parentNode.timelineCharts[i]
|
||||
if (timelineChart.rateScale !== undefined) {
|
||||
if (thisObject.payload.node.id !== timelineChart.rateScale.id) {
|
||||
if (timelineChart.rateScale.payload.isVisible === true) {
|
||||
displaceFactor++
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bottonCorner.x = bottonCorner.x - thisObject.container.frame.width * displaceFactor
|
||||
|
||||
/* Mouse Position Rate Calculation */
|
||||
let ratePoint = {
|
||||
x: 0,
|
||||
y: mouse.position.y
|
||||
}
|
||||
|
||||
thisObject.rate = getRateFromPointAtBrowserCanvas(ratePoint, rateCalculationsContainer, coordinateSystem)
|
||||
|
||||
/* rateScale Positioning */
|
||||
ratePoint = {
|
||||
x: limitingContainer.frame.width,
|
||||
y: 0
|
||||
}
|
||||
|
||||
ratePoint = transformThisPoint(ratePoint, limitingContainer.frame.container)
|
||||
ratePoint.y = mouse.position.y - thisObject.container.frame.height / 2 + thisObject.container.frame.height
|
||||
|
||||
/* Checking against the container limits. */
|
||||
if (ratePoint.x < upCorner.x) { ratePoint.x = upCorner.x }
|
||||
if (ratePoint.x + thisObject.container.frame.width > bottonCorner.x) { ratePoint.x = bottonCorner.x }
|
||||
if (ratePoint.y < upCorner.y + thisObject.container.frame.height) { ratePoint.y = upCorner.y + thisObject.container.frame.height }
|
||||
if (ratePoint.y > bottonCorner.y) { ratePoint.y = bottonCorner.y }
|
||||
|
||||
thisObject.container.frame.position.y = ratePoint.y - thisObject.container.frame.height
|
||||
thisObject.container.frame.position.x = ratePoint.x - thisObject.container.frame.width
|
||||
|
||||
thisObject.isVisible = true
|
||||
thisObject.payload.isVisible = true
|
||||
if (thisObject.container.frame.position.y + thisObject.container.frame.height * 2 > bottonCorner.y ||
|
||||
thisObject.container.frame.position.y - thisObject.container.frame.height * 1 < upCorner.y ||
|
||||
thisObject.container.frame.position.x < upCorner.x
|
||||
) {
|
||||
thisObject.isVisible = false
|
||||
thisObject.payload.isVisible = false
|
||||
}
|
||||
if (thisObject.layersOn === 0) {
|
||||
thisObject.isVisible = false
|
||||
thisObject.payload.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
drawScaleBox()
|
||||
autoScaleButton.draw()
|
||||
if (visible === false) {
|
||||
drawScaleDisplayCover(thisObject.container)
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
if (isMouseOver === true) {
|
||||
drawScaleBox()
|
||||
autoScaleButton.draw()
|
||||
}
|
||||
}
|
||||
|
||||
function drawScaleBox () {
|
||||
if (thisObject.rate === undefined) { return }
|
||||
if (thisObject.payload === undefined) { return }
|
||||
if (thisObject.payload.node === undefined) { return }
|
||||
|
||||
let rate = thisObject.rate
|
||||
|
||||
if (rate < coordinateSystem.min.y) {
|
||||
rate = coordinateSystem.min.y
|
||||
}
|
||||
if (rate > coordinateSystem.max.y) {
|
||||
rate = coordinateSystem.max.y
|
||||
}
|
||||
|
||||
let label = (rate - Math.trunc(rate)).toFixed(2)
|
||||
let labelArray = label.split('.')
|
||||
let label1 = thisObject.payload.node.payload.parentNode.name
|
||||
let label2 = (Math.trunc(rate)).toLocaleString()
|
||||
let label3 = labelArray[1]
|
||||
if (label3 === undefined) { label3 = '00' }
|
||||
|
||||
let icon1 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.type)
|
||||
let icon2 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type)
|
||||
|
||||
let backgroundColor = UI_COLOR.BLACK
|
||||
|
||||
if (scaleDisplayTimer > 0) {
|
||||
label2 = (coordinateSystem.scale.y * 10000).toFixed(2)
|
||||
label3 = 'SCALE'
|
||||
}
|
||||
|
||||
drawScaleDisplay(label1, label2, label3, 0, 0, 0, icon1, icon2, thisObject.container, backgroundColor)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,350 @@
|
||||
function newTimeFrameScale () {
|
||||
const MODULE_NAME = 'Time Frame Scale'
|
||||
|
||||
let thisObject = {
|
||||
timeFrame: undefined,
|
||||
container: undefined,
|
||||
fitFunction: undefined,
|
||||
payload: undefined,
|
||||
isVisible: true,
|
||||
layersOn: undefined,
|
||||
onMouseOverSomeTimeMachineContainer: onMouseOverSomeTimeMachineContainer,
|
||||
draw: draw,
|
||||
drawForeground: drawForeground,
|
||||
physics: physics,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
const FILES_PERIOD_DEFAULT_VALUE = 0
|
||||
const TIME_PERIOD_DEFAULT_VALUE = 0
|
||||
const MIN_HEIGHT = 50
|
||||
|
||||
let previousIsVisible = true
|
||||
let greyOutCoverLayer = false
|
||||
let isMouseOver
|
||||
|
||||
let filePeriodIndex = FILES_PERIOD_DEFAULT_VALUE
|
||||
let timeFrameIndex = TIME_PERIOD_DEFAULT_VALUE
|
||||
let timeFramesMasterArray = [marketFilesPeriods, dailyFilePeriods]
|
||||
let timeFrameLabel = '24-hs'
|
||||
|
||||
let onMouseWheelEventSubscriptionId
|
||||
let onViewportZoomChangedEventSubscriptionId
|
||||
let onMouseOverEventSubscriptionId
|
||||
let onMouseNotOverEventSubscriptionId
|
||||
|
||||
let limitingContainer
|
||||
|
||||
let mouse = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
setupContainer()
|
||||
return thisObject
|
||||
|
||||
function setupContainer () {
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isWheelable = true
|
||||
thisObject.container.detectMouseOver = true
|
||||
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL
|
||||
thisObject.container.frame.height = 40
|
||||
}
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(onMouseWheelEventSubscriptionId)
|
||||
canvas.chartingSpace.viewport.eventHandler.stopListening(onViewportZoomChangedEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.payload = undefined
|
||||
|
||||
limitingContainer = undefined
|
||||
mouse = undefined
|
||||
}
|
||||
|
||||
function initialize (pLimitingContainer) {
|
||||
limitingContainer = pLimitingContainer
|
||||
|
||||
readObjectState()
|
||||
newTimeFrame()
|
||||
|
||||
onMouseWheelEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel)
|
||||
onViewportZoomChangedEventSubscriptionId = canvas.chartingSpace.viewport.eventHandler.listenToEvent('Zoom Changed', onViewportZoomChanged)
|
||||
onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
||||
}
|
||||
|
||||
function onMouseOverSomeTimeMachineContainer (event) {
|
||||
if (event.containerId === undefined) {
|
||||
/* This happens when the mouse over was not at the instance of a certain scale, but anywhere else. */
|
||||
greyOutCoverLayer = false
|
||||
} else {
|
||||
if (event.containerId === thisObject.container.id) {
|
||||
greyOutCoverLayer = false
|
||||
} else {
|
||||
greyOutCoverLayer = true
|
||||
turnOnCounter = 0
|
||||
}
|
||||
}
|
||||
mouse = {
|
||||
position: {
|
||||
x: event.x,
|
||||
y: event.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
isMouseOver = true
|
||||
event.containerId = thisObject.container.id
|
||||
thisObject.container.eventHandler.raiseEvent('onMouseOverScale', event)
|
||||
}
|
||||
|
||||
function onMouseNotOver () {
|
||||
isMouseOver = false
|
||||
}
|
||||
|
||||
function physics () {
|
||||
readObjectState()
|
||||
positioningPhysics()
|
||||
zoomOutPhysics()
|
||||
}
|
||||
|
||||
function zoomOutPhysics () {
|
||||
if ((thisObject.isVisible === true && previousIsVisible === false) && (canvas.chartingSpace.viewport.zoomTargetLevel >= ZOOM_OUT_THRESHOLD_FOR_CHANGING_TIME_FRAME)) {
|
||||
let event = {}
|
||||
event.timeFrame = thisObject.timeFrame
|
||||
thisObject.container.eventHandler.raiseEvent('Time Frame Value Changed', event)
|
||||
previousIsVisible = true
|
||||
}
|
||||
if ((thisObject.isVisible === false && previousIsVisible === true) || (canvas.chartingSpace.viewport.zoomTargetLevel < ZOOM_OUT_THRESHOLD_FOR_CHANGING_TIME_FRAME)) {
|
||||
let event = {}
|
||||
event.timeFrame = ONE_DAY_IN_MILISECONDS
|
||||
thisObject.container.eventHandler.raiseEvent('Time Frame Value Changed', event)
|
||||
previousIsVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
function positioningPhysics () {
|
||||
/* Container Limits */
|
||||
|
||||
let upCorner = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let bottonCorner = {
|
||||
x: limitingContainer.frame.width,
|
||||
y: limitingContainer.frame.height
|
||||
}
|
||||
|
||||
upCorner = transformThisPoint(upCorner, limitingContainer)
|
||||
bottonCorner = transformThisPoint(bottonCorner, limitingContainer)
|
||||
|
||||
upCorner = limitingContainer.fitFunction(upCorner, true)
|
||||
bottonCorner = limitingContainer.fitFunction(bottonCorner, true)
|
||||
|
||||
/* We will check if we need to change the bottom because of being one of many scales of the same type */
|
||||
let displaceFactor = 0
|
||||
if (thisObject.payload.parentNode === undefined) { return } // This happens when in the process of deleting the scale, timeline chart or time machine.
|
||||
if (thisObject.payload.parentNode.type === 'Timeline Chart') {
|
||||
if (thisObject.payload.parentNode.payload.parentNode !== undefined) {
|
||||
if (thisObject.payload.parentNode.payload.parentNode.timeFrameScale !== undefined) {
|
||||
if (thisObject.payload.parentNode.payload.parentNode.timeFrameScale.payload.isVisible === true) {
|
||||
displaceFactor++
|
||||
}
|
||||
}
|
||||
for (let i = 0; i < thisObject.payload.parentNode.payload.parentNode.timelineCharts.length; i++) {
|
||||
let timelineChart = thisObject.payload.parentNode.payload.parentNode.timelineCharts[i]
|
||||
if (timelineChart.timeFrameScale !== undefined) {
|
||||
if (thisObject.payload.node.id !== timelineChart.timeFrameScale.id) {
|
||||
if (timelineChart.timeFrameScale.payload.isVisible === true) {
|
||||
displaceFactor++
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bottonCorner.y = bottonCorner.y - thisObject.container.frame.height * displaceFactor
|
||||
|
||||
/* Positioning */
|
||||
timePoint = {
|
||||
x: 0,
|
||||
y: limitingContainer.frame.height
|
||||
}
|
||||
|
||||
timePoint = transformThisPoint(timePoint, limitingContainer.frame.container)
|
||||
timePoint.x = mouse.position.x - thisObject.container.frame.width / 2
|
||||
|
||||
/* Checking against the container limits. */
|
||||
if (timePoint.x < upCorner.x) { timePoint.x = upCorner.x }
|
||||
if (timePoint.x + thisObject.container.frame.width > bottonCorner.x) { timePoint.x = bottonCorner.x - thisObject.container.frame.width }
|
||||
if (timePoint.y < upCorner.y + thisObject.container.frame.height) { timePoint.y = upCorner.y + thisObject.container.frame.height }
|
||||
if (timePoint.y > bottonCorner.y) { timePoint.y = bottonCorner.y }
|
||||
|
||||
thisObject.container.frame.position.x = timePoint.x
|
||||
thisObject.container.frame.position.y = timePoint.y - thisObject.container.frame.height
|
||||
|
||||
thisObject.isVisible = true
|
||||
thisObject.payload.isVisible = true
|
||||
if (thisObject.container.frame.position.y + thisObject.container.frame.height > bottonCorner.y ||
|
||||
thisObject.container.frame.position.y - thisObject.container.frame.height * 3 < upCorner.y ||
|
||||
thisObject.container.frame.position.x < upCorner.x) {
|
||||
thisObject.isVisible = false
|
||||
thisObject.payload.isVisible = false
|
||||
}
|
||||
if (thisObject.layersOn === 0) {
|
||||
thisObject.isVisible = false
|
||||
thisObject.payload.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
function onViewportZoomChanged (event) {
|
||||
if (event !== undefined) { // it is undefined when the level is just being animated.
|
||||
if (event.shiftKey === false) { return } // with no shft key pressed, no automatic change in scale
|
||||
let currentTimeFrame = thisObject.timeFrame
|
||||
let timeFrame = recalculatePeriod(event.newLevel)
|
||||
if (timeFrame !== currentTimeFrame) {
|
||||
for (let i = 0; i < timeFramesMasterArray.length; i++) {
|
||||
let timeFrameArray = timeFramesMasterArray[i]
|
||||
for (let j = 0; j < timeFrameArray.length; j++) {
|
||||
let record = timeFrameArray[j]
|
||||
if (timeFrame === record[0]) {
|
||||
filePeriodIndex = i
|
||||
timeFrameIndex = j
|
||||
}
|
||||
}
|
||||
}
|
||||
newTimeFrame()
|
||||
saveObjectState()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseWheel (event) {
|
||||
delta = event.wheelDelta
|
||||
if (delta < 0) {
|
||||
timeFrameIndex--
|
||||
if (timeFrameIndex < 0) {
|
||||
filePeriodIndex--
|
||||
if (filePeriodIndex < 0) {
|
||||
filePeriodIndex = 0
|
||||
timeFrameIndex = 0
|
||||
} else {
|
||||
timeFrameIndex = timeFramesMasterArray[filePeriodIndex].length - 1
|
||||
}
|
||||
}
|
||||
} else {
|
||||
timeFrameIndex++
|
||||
if (timeFrameIndex > timeFramesMasterArray[filePeriodIndex].length - 1) {
|
||||
filePeriodIndex++
|
||||
if (filePeriodIndex > timeFramesMasterArray.length - 1) {
|
||||
filePeriodIndex = timeFramesMasterArray.length - 1
|
||||
timeFrameIndex = timeFramesMasterArray[filePeriodIndex].length - 1
|
||||
} else {
|
||||
timeFrameIndex = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newTimeFrame()
|
||||
saveObjectState()
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
|
||||
function saveObjectState () {
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
code.value = timeFrameLabel
|
||||
thisObject.payload.node.code = JSON.stringify(code)
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function readObjectState () {
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
if (code.value !== timeFrameLabel) {
|
||||
let found = false
|
||||
for (let i = 0; i < timeFramesMasterArray.length; i++) {
|
||||
let timeFrameArray = timeFramesMasterArray[i]
|
||||
for (let j = 0; j < timeFrameArray.length; j++) {
|
||||
let record = timeFrameArray[j]
|
||||
if (code.value === record[1]) {
|
||||
filePeriodIndex = i
|
||||
timeFrameIndex = j
|
||||
found = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if (found === true) {
|
||||
newTimeFrame()
|
||||
} else {
|
||||
saveObjectState()
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function newTimeFrame () {
|
||||
let timeFrameArray = timeFramesMasterArray[filePeriodIndex]
|
||||
thisObject.timeFrame = timeFrameArray[timeFrameIndex][0]
|
||||
timeFrameLabel = timeFrameArray[timeFrameIndex][1]
|
||||
// console.log('NEW TIME FRAME LABEL', timeFrameLabel)
|
||||
let event = {}
|
||||
event.timeFrame = thisObject.timeFrame
|
||||
thisObject.container.eventHandler.raiseEvent('Time Frame Value Changed', event)
|
||||
}
|
||||
|
||||
function draw () {
|
||||
drawScaleBox()
|
||||
if (greyOutCoverLayer === true) {
|
||||
drawScaleDisplayCover(thisObject.container)
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
if (isMouseOver === true) {
|
||||
drawScaleBox()
|
||||
}
|
||||
}
|
||||
|
||||
function drawScaleBox () {
|
||||
if (timeFrameLabel === undefined) { return }
|
||||
|
||||
let label = timeFrameLabel.split('-')
|
||||
let label1 = thisObject.payload.node.payload.parentNode.name
|
||||
let label2 = label[0]
|
||||
let label3 = label[1].toUpperCase()
|
||||
|
||||
let icon1 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.type)
|
||||
let icon2 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type)
|
||||
|
||||
let backgroundColor = UI_COLOR.BLACK
|
||||
|
||||
drawScaleDisplay(label1, label2, label3, 0, 0, 0, icon1, icon2, thisObject.container, backgroundColor)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
function newTimeScale () {
|
||||
const MODULE_NAME = 'Time Scale'
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
date: undefined,
|
||||
fitFunction: undefined,
|
||||
payload: undefined,
|
||||
isVisible: true,
|
||||
fromDate: undefined,
|
||||
toDate: undefined,
|
||||
onMouseOverSomeTimeMachineContainer: onMouseOverSomeTimeMachineContainer,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
drawForeground: drawForeground,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isWheelable = true
|
||||
thisObject.container.detectMouseOver = true
|
||||
|
||||
thisObject.container.frame.width = UI_PANEL.WIDTH.NORMAL
|
||||
thisObject.container.frame.height = 40
|
||||
|
||||
let visible = true
|
||||
let autoScaleButton
|
||||
let isMouseOver
|
||||
|
||||
let onMouseWheelEventSubscriptionId
|
||||
let onMouseOverEventSubscriptionId
|
||||
let onMouseNotOverEventSubscriptionId
|
||||
let onScaleChangedEventSubscriptionId
|
||||
|
||||
let coordinateSystem
|
||||
let limitingContainer
|
||||
|
||||
let mouse = {
|
||||
position: {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(onMouseWheelEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseOverEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(onMouseNotOverEventSubscriptionId)
|
||||
coordinateSystem.eventHandler.stopListening(onScaleChangedEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.fitFunction = undefined
|
||||
thisObject.payload = undefined
|
||||
|
||||
coordinateSystem = undefined
|
||||
limitingContainer = undefined
|
||||
mouse = undefined
|
||||
|
||||
autoScaleButton.finalize()
|
||||
autoScaleButton = undefined
|
||||
}
|
||||
|
||||
function initialize (pCoordinateSystem, pLimitingContainer) {
|
||||
coordinateSystem = pCoordinateSystem
|
||||
limitingContainer = pLimitingContainer
|
||||
|
||||
onMouseWheelEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel)
|
||||
onMouseOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
||||
onMouseNotOverEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
||||
onScaleChangedEventSubscriptionId = coordinateSystem.eventHandler.listenToEvent('Scale Changed', onScaleChanged)
|
||||
|
||||
autoScaleButton = newAutoScaleButton()
|
||||
autoScaleButton.container.connectToParent(thisObject.container)
|
||||
autoScaleButton.initialize('X', coordinateSystem)
|
||||
|
||||
readObjectState()
|
||||
}
|
||||
|
||||
function onMouseOverSomeTimeMachineContainer (event) {
|
||||
if (event.containerId === undefined) {
|
||||
/* This happens when the mouse over was not at the instance of a certain scale, but anywhere else. */
|
||||
visible = true
|
||||
} else {
|
||||
if (event.containerId === thisObject.container.id) {
|
||||
visible = true
|
||||
} else {
|
||||
visible = false
|
||||
turnOnCounter = 0
|
||||
}
|
||||
}
|
||||
mouse = {
|
||||
position: {
|
||||
x: event.x,
|
||||
y: event.y
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onMouseOver (event) {
|
||||
isMouseOver = true
|
||||
event.containerId = thisObject.container.id
|
||||
thisObject.container.eventHandler.raiseEvent('onMouseOverScale', event)
|
||||
}
|
||||
|
||||
function onMouseNotOver () {
|
||||
isMouseOver = false
|
||||
}
|
||||
|
||||
function onMouseWheel (event) {
|
||||
if (event.shiftKey === true) {
|
||||
autoScaleButton.container.eventHandler.raiseEvent('onMouseWheel', event)
|
||||
return
|
||||
}
|
||||
let factor
|
||||
let morePower = 10
|
||||
if (event.buttons === 4) { morePower = 1 } // Mouse wheel pressed.
|
||||
|
||||
let delta = event.wheelDelta
|
||||
if (delta < 0) {
|
||||
factor = -0.01 * morePower
|
||||
} else {
|
||||
factor = 0.01 * morePower
|
||||
}
|
||||
|
||||
coordinateSystem.zoomX(factor, event, limitingContainer, MODULE_NAME)
|
||||
}
|
||||
|
||||
function onScaleChanged () {
|
||||
saveObjectState()
|
||||
}
|
||||
|
||||
function getContainer (point, purpose) {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
|
||||
function saveObjectState () {
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
code.fromDate = (new Date(coordinateSystem.min.x)).toISOString()
|
||||
code.toDate = (new Date(coordinateSystem.max.x)).toISOString()
|
||||
code.autoMinScale = coordinateSystem.autoMinXScale
|
||||
code.autoMaxScale = coordinateSystem.autoMaxXScale
|
||||
thisObject.payload.node.code = JSON.stringify(code, null, 4)
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function readObjectState () {
|
||||
try {
|
||||
let code = JSON.parse(thisObject.payload.node.code)
|
||||
|
||||
if (
|
||||
(isNaN(Date.parse(code.fromDate)) || code.fromDate === null || code.fromDate === undefined) ||
|
||||
(isNaN(Date.parse(code.toDate)) || code.toDate === null || code.toDate === undefined)
|
||||
) {
|
||||
// not using this value
|
||||
} else {
|
||||
if (thisObject.fromDate !== Date.parse(code.fromDate) || thisObject.toDate !== Date.parse(code.toDate)) {
|
||||
thisObject.fromDate = Date.parse(code.fromDate)
|
||||
thisObject.toDate = Date.parse(code.toDate)
|
||||
coordinateSystem.min.x = thisObject.fromDate
|
||||
coordinateSystem.max.x = thisObject.toDate
|
||||
|
||||
if (code.autoMinScale !== undefined && (code.autoMinScale === true || code.autoMinScale === false) && code.autoMaxScale !== undefined && (code.autoMaxScale === true || code.autoMaxScale === false)) {
|
||||
coordinateSystem.autoMinXScale = code.autoMinScale
|
||||
coordinateSystem.autoMaxXScale = code.autoMaxScale
|
||||
autoScaleButton.setStatus(code.autoMinScale, code.autoMaxScale)
|
||||
}
|
||||
coordinateSystem.recalculateScale()
|
||||
}
|
||||
}
|
||||
saveObjectState() // this overrides any invalid value at the config.
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
readObjectState()
|
||||
positioningPhysics()
|
||||
}
|
||||
|
||||
function positioningPhysics () {
|
||||
/* Container Limits */
|
||||
|
||||
let upCorner = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let bottonCorner = {
|
||||
x: limitingContainer.frame.width,
|
||||
y: limitingContainer.frame.height
|
||||
}
|
||||
|
||||
upCorner = transformThisPoint(upCorner, limitingContainer)
|
||||
bottonCorner = transformThisPoint(bottonCorner, limitingContainer)
|
||||
|
||||
upCorner = limitingContainer.fitFunction(upCorner, true)
|
||||
bottonCorner = limitingContainer.fitFunction(bottonCorner, true)
|
||||
|
||||
/* Mouse Position Date Calculation */
|
||||
let timePoint = {
|
||||
x: mouse.position.x,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let mouseDate = getDateFromPointAtBrowserCanvas(timePoint, limitingContainer, coordinateSystem)
|
||||
|
||||
thisObject.date = new Date(mouseDate)
|
||||
|
||||
/* timeScale Positioning */
|
||||
timePoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
timePoint = transformThisPoint(timePoint, limitingContainer.frame.container)
|
||||
timePoint.x = mouse.position.x - thisObject.container.frame.width / 2
|
||||
|
||||
/* Checking against the container limits. */
|
||||
if (timePoint.x < upCorner.x) { timePoint.x = upCorner.x }
|
||||
if (timePoint.x + thisObject.container.frame.width > bottonCorner.x) { timePoint.x = bottonCorner.x - thisObject.container.frame.width }
|
||||
if (timePoint.y < upCorner.y) { timePoint.y = upCorner.y }
|
||||
if (timePoint.y + thisObject.container.frame.height > bottonCorner.y) { timePoint.y = bottonCorner.y - thisObject.container.frame.height }
|
||||
|
||||
thisObject.container.frame.position.x = timePoint.x
|
||||
thisObject.container.frame.position.y = timePoint.y
|
||||
|
||||
thisObject.isVisible = true
|
||||
if (thisObject.container.frame.position.y + thisObject.container.frame.height * 4 > bottonCorner.y ||
|
||||
thisObject.container.frame.position.y < upCorner.y ||
|
||||
thisObject.container.frame.position.x < upCorner.x) {
|
||||
thisObject.isVisible = false
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
drawScaleBox()
|
||||
autoScaleButton.draw()
|
||||
if (visible === false) {
|
||||
drawScaleDisplayCover(thisObject.container)
|
||||
}
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
if (isMouseOver === true) {
|
||||
drawScaleBox()
|
||||
autoScaleButton.draw()
|
||||
}
|
||||
}
|
||||
|
||||
function drawScaleBox () {
|
||||
if (thisObject.date === undefined) { return }
|
||||
|
||||
let label = thisObject.date.toUTCString()
|
||||
let labelArray = label.split(' ')
|
||||
let label1 = thisObject.payload.node.payload.parentNode.name
|
||||
let label2 = labelArray[1] + ' ' + labelArray[2] + ' ' + labelArray[3]
|
||||
let label3 = labelArray[4]
|
||||
|
||||
let icon1 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.payload.parentNode.type)
|
||||
let icon2 = canvas.designSpace.iconByUiObjectType.get(thisObject.payload.node.type)
|
||||
|
||||
let backgroundColor = UI_COLOR.BLACK
|
||||
|
||||
drawScaleDisplay(label1, label2, label3, 0, 0, 0, icon1, icon2, thisObject.container, backgroundColor)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,403 @@
|
||||
/*
|
||||
|
||||
The chart space is where all the charts live. What this space contains is a type of object we call Time Machine. A Time Machine is a cointainer of
|
||||
charts objects. The idea is simple, all the chart objects are ploted according to the time of the Time Machine object. When the time of the Time Machine
|
||||
changes, then all charts in it are replotted with the corresponging data.
|
||||
|
||||
*/
|
||||
|
||||
function newChartingSpace () {
|
||||
const MODULE_NAME = 'Charting Space'
|
||||
const ERROR_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
let thisObject = {
|
||||
visible: undefined,
|
||||
container: undefined,
|
||||
inViewport: undefined,
|
||||
timeMachines: undefined,
|
||||
viewport: undefined,
|
||||
payload: undefined,
|
||||
reset: reset,
|
||||
oneScreenUp: oneScreenUp,
|
||||
oneScreenDown: oneScreenDown,
|
||||
oneScreenLeft: oneScreenLeft,
|
||||
oneScreenRight: oneScreenRight,
|
||||
fitFunction: fitFunction,
|
||||
isThisPointVisible: isThisPointVisible,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
let canvasBrowserResizedEventSubscriptionId
|
||||
let timeMachinesMap
|
||||
let syncWithDesignerLoop
|
||||
|
||||
const PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT = 25
|
||||
|
||||
initialSetup()
|
||||
return thisObject
|
||||
|
||||
function reset () {
|
||||
finalize()
|
||||
initialSetup()
|
||||
initialize()
|
||||
}
|
||||
|
||||
function initialSetup () {
|
||||
thisObject.timeMachines = []
|
||||
thisObject.visible = true
|
||||
timeMachinesMap = new Map()
|
||||
syncWithDesignerLoop = 0
|
||||
thisObject.inViewport = new Map()
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
|
||||
thisObject.container.isDraggeable = false
|
||||
thisObject.container.isWheelable = false
|
||||
}
|
||||
|
||||
function finalize () {
|
||||
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
||||
let timeMachine = thisObject.timeMachines[i]
|
||||
timeMachine.finalize()
|
||||
}
|
||||
|
||||
thisObject.inViewport = undefined
|
||||
thisObject.timeMachines = undefined
|
||||
timeMachinesMap = undefined
|
||||
|
||||
thisObject.container.eventHandler.stopListening(canvasBrowserResizedEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
|
||||
thisObject.viewport.finalize()
|
||||
thisObject.viewport = undefined
|
||||
|
||||
thisObject.payload = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
let rootNodes = canvas.designSpace.workspace.workspaceNode.rootNodes
|
||||
for (let i = 0; i < rootNodes.length; i++) {
|
||||
let rootNode = rootNodes[i]
|
||||
if (rootNode.type === 'Charting Space') {
|
||||
thisObject.payload = rootNode.payload
|
||||
}
|
||||
}
|
||||
|
||||
if (thisObject.payload === undefined) {
|
||||
if (ERROR_LOG === true) { logger.write('[WARN] initialize -> There must exist a Charting Space at the Design Space in order to enable Charts. PLease create one and refreash your browser. ') }
|
||||
return
|
||||
}
|
||||
|
||||
thisObject.viewport = newViewport()
|
||||
if (thisObject.payload.node.viewport === undefined) {
|
||||
if (ERROR_LOG === true) { logger.write('[WARN] initialize -> There must exist a Viewport node attached to the Charting Space node in order for the Viewport save its properties. ') }
|
||||
return
|
||||
} else {
|
||||
thisObject.viewport.payload = thisObject.payload.node.viewport.payload
|
||||
}
|
||||
|
||||
thisObject.viewport.initialize()
|
||||
|
||||
canvasBrowserResizedEventSubscriptionId = canvas.eventHandler.listenToEvent('Browser Resized', resize)
|
||||
}
|
||||
|
||||
function getContainer (point, purpose) {
|
||||
if (thisObject.visible !== true) { return }
|
||||
if (thisObject.viewport.isThisPointInViewport(point) === false) { return }
|
||||
|
||||
let container
|
||||
|
||||
/* This first step is not actually to get the container but to allow things to be turned off in case there is a switch of focus from one time machine to another */
|
||||
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
||||
container = thisObject.timeMachines[i].container
|
||||
if (purpose === GET_CONTAINER_PURPOSE.MOUSE_OVER) {
|
||||
container.eventHandler.raiseEvent('onMouseNotOver')
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
Now we see which is the inner most container that has it.
|
||||
In this case we will ask all the time machines even if one of them already returned a matching container.
|
||||
This will help the others know they are not on ofcus
|
||||
*/
|
||||
let containerFound
|
||||
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
||||
container = thisObject.timeMachines[i].getContainer(point, purpose)
|
||||
if (container !== undefined) {
|
||||
if (purpose !== undefined) {
|
||||
containerFound = container
|
||||
} else {
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
containerFound = container
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (containerFound !== undefined) {
|
||||
containerFound.space = 'Charting Space'
|
||||
return containerFound
|
||||
}
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
thisObject.container.space = 'Charting Space'
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function resize () {
|
||||
thisObject.viewport.resize()
|
||||
thisObject.container.frame.width = browserCanvas.width
|
||||
thisObject.container.frame.height = COCKPIT_SPACE_POSITION
|
||||
}
|
||||
|
||||
function oneScreenUp () {
|
||||
let displaceVector = {
|
||||
x: 0,
|
||||
y: browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100
|
||||
}
|
||||
|
||||
canvas.chartingSpace.viewport.displace(displaceVector)
|
||||
}
|
||||
|
||||
function oneScreenDown () {
|
||||
let displaceVector = {
|
||||
x: 0,
|
||||
y: -browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100
|
||||
}
|
||||
|
||||
canvas.chartingSpace.viewport.displace(displaceVector)
|
||||
}
|
||||
|
||||
function oneScreenLeft () {
|
||||
let displaceVector = {
|
||||
x: browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100,
|
||||
y: 0
|
||||
}
|
||||
|
||||
canvas.chartingSpace.viewport.displace(displaceVector, true)
|
||||
}
|
||||
|
||||
function oneScreenRight () {
|
||||
let displaceVector = {
|
||||
x: -browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100,
|
||||
y: 0
|
||||
}
|
||||
|
||||
canvas.chartingSpace.viewport.displace(displaceVector, true)
|
||||
}
|
||||
|
||||
function fitFunction (point, fullVisible) {
|
||||
/* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */
|
||||
|
||||
let returnPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
if (point.x > browserCanvas.width) {
|
||||
returnPoint.x = browserCanvas.width
|
||||
}
|
||||
|
||||
if (point.x < 0) {
|
||||
returnPoint.x = 0
|
||||
}
|
||||
|
||||
if (fullVisible === true) {
|
||||
if (point.y > COCKPIT_SPACE_POSITION) {
|
||||
returnPoint.y = COCKPIT_SPACE_POSITION
|
||||
}
|
||||
} else {
|
||||
if (point.y > COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT / 2) {
|
||||
returnPoint.y = COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT / 2
|
||||
}
|
||||
}
|
||||
|
||||
if (point.y < TOP_SPACE_HEIGHT) {
|
||||
returnPoint.y = TOP_SPACE_HEIGHT
|
||||
}
|
||||
|
||||
return returnPoint
|
||||
}
|
||||
|
||||
function isThisPointVisible (point) {
|
||||
if (point.x > browserCanvas.width) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (point.x < 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (point.y > COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT / 2) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (point.y < 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (thisObject.viewport !== undefined) {
|
||||
thisObject.viewport.physics()
|
||||
}
|
||||
|
||||
thisObjectPhysics()
|
||||
childrenPhysics()
|
||||
syncWithDesigner()
|
||||
}
|
||||
|
||||
function syncWithDesigner () {
|
||||
if (thisObject.payload === undefined) { return }
|
||||
if (thisObject.payload.node === undefined) { return }
|
||||
syncWithDesignerLoop = syncWithDesignerLoop + 0.00000000001
|
||||
|
||||
if (thisObject.payload.node.dashboards !== undefined) {
|
||||
for (let i = 0; i < thisObject.payload.node.dashboards.length; i++) {
|
||||
let dashboard = thisObject.payload.node.dashboards[i]
|
||||
if (dashboard.timeMachines !== undefined) {
|
||||
for (let j = 0; j < dashboard.timeMachines.length; j++) {
|
||||
let node = dashboard.timeMachines[j]
|
||||
let timeMachine = timeMachinesMap.get(node.id)
|
||||
if (timeMachine === undefined) {
|
||||
/* The time machine node is new, thus we need to initialize a new timeMachine */
|
||||
initializeTimeMachine(node, syncWithDesignerLoop)
|
||||
} else {
|
||||
/* The time machine already exists, we tag it as existing at the current loop. */
|
||||
timeMachine.syncWithDesignerLoop = syncWithDesignerLoop
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* We check all the timeMachines we have to see if we need to remove any of them */
|
||||
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
||||
let timeMachine = thisObject.timeMachines[i]
|
||||
if (timeMachine.syncWithDesignerLoop < syncWithDesignerLoop) {
|
||||
/* Must be removed */
|
||||
timeMachine.finalize()
|
||||
timeMachinesMap.delete(timeMachine.nodeId)
|
||||
thisObject.timeMachines.splice(i, 1)
|
||||
/* We remove one at the time */
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
function initializeTimeMachine (node, syncWithDesignerLoop) {
|
||||
if (node.payload === undefined) { return } // not ready to consider this Time Machine
|
||||
if (node.payload.uiObject === undefined) { return }
|
||||
|
||||
let timeMachine = newTimeMachine()
|
||||
timeMachine.syncWithDesignerLoop = syncWithDesignerLoop
|
||||
timeMachine.payload = node.payload
|
||||
timeMachine.nodeId = node.id
|
||||
|
||||
timeMachinesMap.set(node.id, timeMachine)
|
||||
timeMachine.payload.uiObject.setValue('Loading...')
|
||||
|
||||
/* Setting up the new time machine. */
|
||||
timeMachine.container.frame.position.x = browserCanvas.width / 2 - TIME_MACHINE_WIDTH / 2
|
||||
timeMachine.container.frame.position.y = browserCanvas.height / 2 - TIME_MACHINE_HEIGHT / 2
|
||||
timeMachine.initialize(onTimeMachineInitialized)
|
||||
|
||||
function onTimeMachineInitialized () {
|
||||
thisObject.timeMachines.push(timeMachine)
|
||||
timeMachine.payload.uiObject.setValue('')
|
||||
if (canvas.chartingSpace.viewport !== undefined) {
|
||||
canvas.chartingSpace.viewport.raiseEvents() // These events will impacts on objects just initialized.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function thisObjectPhysics () {
|
||||
thisObject.container.frame.height = COCKPIT_SPACE_POSITION
|
||||
|
||||
if (thisObject.container.frame.height <= 0 / 100) {
|
||||
thisObject.visible = false
|
||||
} else {
|
||||
thisObject.visible = true
|
||||
}
|
||||
|
||||
if (canvas.chartingSpace.viewport !== undefined) {
|
||||
canvas.chartingSpace.viewport.resize()
|
||||
}
|
||||
}
|
||||
|
||||
function childrenPhysics () {
|
||||
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
||||
let timeMachine = thisObject.timeMachines[i]
|
||||
if (timeMachine.payload.node === undefined) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (timeMachine.container.frame.isInViewPort()) {
|
||||
thisObject.inViewport.set(timeMachine.payload.node.id, timeMachine)
|
||||
} else {
|
||||
thisObject.inViewport.delete(timeMachine.payload.node.id)
|
||||
}
|
||||
timeMachine.physics()
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
drawSpaceBackground()
|
||||
|
||||
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
||||
let timeMachine = thisObject.timeMachines[thisObject.timeMachines.length - i - 1]
|
||||
timeMachine.drawBackground()
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.visible !== true) { return }
|
||||
drawBackground()
|
||||
|
||||
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
||||
let timeMachine = thisObject.timeMachines[thisObject.timeMachines.length - i - 1]
|
||||
timeMachine.draw()
|
||||
}
|
||||
|
||||
drawForeground()
|
||||
}
|
||||
|
||||
function drawForeground () {
|
||||
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
||||
let timeMachine = thisObject.timeMachines[thisObject.timeMachines.length - i - 1]
|
||||
timeMachine.drawForeground()
|
||||
}
|
||||
}
|
||||
|
||||
function drawSpaceBackground () {
|
||||
let opacity = '1'
|
||||
let fromPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let toPoint = {
|
||||
x: browserCanvas.width,
|
||||
y: COCKPIT_SPACE_POSITION
|
||||
}
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.rect(fromPoint.x, fromPoint.y, toPoint.x - fromPoint.x, toPoint.y - fromPoint.y)
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')'
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,314 @@
|
||||
|
||||
function newCockpitSpace () {
|
||||
const MODULE_NAME = 'CockpitSpace'
|
||||
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
status: 'BOTTOM',
|
||||
assetBalances: undefined,
|
||||
fullscreen: undefined,
|
||||
toTop: toTop,
|
||||
toBottom: toBottom,
|
||||
toMiddle: toMiddle,
|
||||
moveUp: moveUp,
|
||||
moveDown: moveDown,
|
||||
draw: draw,
|
||||
physics: physics,
|
||||
getContainer: getContainer,
|
||||
finalize: finalize,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.detectMouseOver = true
|
||||
thisObject.container.isDraggeable = true
|
||||
thisObject.container.notDraggingOnX = true
|
||||
|
||||
controlsMap = new Map()
|
||||
resize()
|
||||
|
||||
let selfMouseClickEventSubscriptionId
|
||||
let canvasBrowserResizedEventSubscriptionId
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(selfMouseClickEventSubscriptionId)
|
||||
thisObject.container.eventHandler.stopListening(canvasBrowserResizedEventSubscriptionId)
|
||||
|
||||
thisObject.container.finalize()
|
||||
thisObject.container = undefined
|
||||
thisObject.assetBalances = undefined
|
||||
thisObject.fullscreen = undefined
|
||||
}
|
||||
|
||||
function initialize () {
|
||||
thisObject.container.frame.position.x = 0
|
||||
|
||||
let INITIAL_POSITION
|
||||
if (canvas.designSpace.workspace.enabled === true) {
|
||||
let localStorage = window.localStorage.getItem(MODULE_NAME)
|
||||
if (localStorage !== null) {
|
||||
storage = JSON.parse(localStorage)
|
||||
thisObject.container.frame.position.y = storage.spacePosition
|
||||
} else {
|
||||
INITIAL_POSITION = 55
|
||||
thisObject.container.frame.position.y = browserCanvas.height * INITIAL_POSITION / 100 - COCKPIT_SPACE_HEIGHT
|
||||
}
|
||||
} else {
|
||||
INITIAL_POSITION = 100
|
||||
thisObject.container.frame.position.y = browserCanvas.height * INITIAL_POSITION / 100 - COCKPIT_SPACE_HEIGHT
|
||||
}
|
||||
|
||||
canvasBrowserResizedEventSubscriptionId = canvas.eventHandler.listenToEvent('Browser Resized', resize)
|
||||
selfMouseClickEventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseClick', onMouseClick)
|
||||
|
||||
thisObject.assetBalances = newQuotedAssetalances()
|
||||
thisObject.assetBalances.initialize()
|
||||
|
||||
thisObject.fullscreen = newFullScreen()
|
||||
thisObject.fullscreen.container.connectToParent(thisObject.container)
|
||||
thisObject.fullscreen.initialize()
|
||||
}
|
||||
|
||||
function onMouseClick (event) {
|
||||
|
||||
}
|
||||
|
||||
function resize () {
|
||||
thisObject.container.frame.width = browserCanvas.width
|
||||
thisObject.container.frame.height = COCKPIT_SPACE_HEIGHT
|
||||
|
||||
switch (thisObject.status) {
|
||||
case 'BOTTOM': {
|
||||
thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT
|
||||
break
|
||||
}
|
||||
case 'TOP': {
|
||||
thisObject.container.frame.position.y = TOP_SPACE_HEIGHT
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function toTop () {
|
||||
thisObject.container.frame.position.y = COCKPIT_SPACE_HEIGHT
|
||||
}
|
||||
|
||||
function toBottom () {
|
||||
thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT
|
||||
}
|
||||
|
||||
function toMiddle () {
|
||||
thisObject.container.frame.position.y = browserCanvas.height / 2 - COCKPIT_SPACE_HEIGHT
|
||||
}
|
||||
|
||||
function moveUp () {
|
||||
thisObject.container.frame.position.y = thisObject.container.frame.position.y - COCKPIT_SPACE_HEIGHT * 2
|
||||
if (thisObject.container.frame.position.y < 0) {
|
||||
thisObject.container.frame.position.y = 0
|
||||
}
|
||||
}
|
||||
|
||||
function moveDown () {
|
||||
thisObject.container.frame.position.y = thisObject.container.frame.position.y + COCKPIT_SPACE_HEIGHT * 2
|
||||
if (thisObject.container.frame.position.y > browserCanvas.height - COCKPIT_SPACE_HEIGHT) {
|
||||
thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
thisObjectPhysics()
|
||||
childrenPhysics()
|
||||
}
|
||||
|
||||
function childrenPhysics () {
|
||||
thisObject.fullscreen.physics()
|
||||
}
|
||||
function thisObjectPhysics () {
|
||||
/* Check the limits */
|
||||
|
||||
thisObject.status = 'MIDDLE'
|
||||
|
||||
if (canvas.designSpace.workspace.enabled === true) {
|
||||
thisObject.container.isDraggeable = true
|
||||
} else {
|
||||
thisObject.container.isDraggeable = false
|
||||
}
|
||||
|
||||
if (thisObject.container.frame.position.y > browserCanvas.height * 99.5 / 100 - COCKPIT_SPACE_HEIGHT) {
|
||||
thisObject.container.frame.position.y = browserCanvas.height - COCKPIT_SPACE_HEIGHT
|
||||
thisObject.status = 'BOTTOM'
|
||||
canvas.designSpace.makeInvisible()
|
||||
canvas.floatingSpace.makeInvisible()
|
||||
} else {
|
||||
canvas.designSpace.makeVisible()
|
||||
canvas.floatingSpace.makeVisible()
|
||||
}
|
||||
|
||||
if (thisObject.container.frame.position.y < browserCanvas.height * 0.5 / 100 + TOP_SPACE_HEIGHT) {
|
||||
thisObject.container.frame.position.y = TOP_SPACE_HEIGHT
|
||||
thisObject.status = 'TOP'
|
||||
canvas.panelsSpace.visible = false
|
||||
} else {
|
||||
canvas.panelsSpace.visible = true
|
||||
}
|
||||
|
||||
COCKPIT_SPACE_POSITION = thisObject.container.frame.position.y
|
||||
let storage = {
|
||||
spacePosition: COCKPIT_SPACE_POSITION
|
||||
}
|
||||
window.localStorage.setItem(MODULE_NAME, JSON.stringify(storage))
|
||||
if (canvas.chartingSpace.viewport !== undefined) {
|
||||
canvas.chartingSpace.viewport.resize()
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
|
||||
// container = thisObject.fullscreen.getContainer(point)
|
||||
// if (container !== undefined) { return container }
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
||||
thisObject.container.space = 'Cockpit Space'
|
||||
return thisObject.container
|
||||
} else {
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
thisObject.container.frame.draw(false, false)
|
||||
|
||||
drawBackground()
|
||||
|
||||
thisObject.assetBalances.draw()
|
||||
// thisObject.fullscreen.draw()
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
let opacity = 1
|
||||
|
||||
let zeroPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
const RED_LINE_HIGHT = 2
|
||||
|
||||
zeroPoint = thisObject.container.frame.frameThisPoint(zeroPoint)
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.rect(zeroPoint.x, zeroPoint.y, thisObject.container.frame.width, thisObject.container.frame.height)
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.DARK_TURQUOISE + ', ' + opacity + ')'
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.rect(zeroPoint.x, zeroPoint.y, thisObject.container.frame.width, RED_LINE_HIGHT)
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.rect(zeroPoint.x, zeroPoint.y - RED_LINE_HIGHT + thisObject.container.frame.height, thisObject.container.frame.width, RED_LINE_HIGHT)
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
|
||||
if (canvas.designSpace.workspace.enabled === true) {
|
||||
arrow()
|
||||
}
|
||||
}
|
||||
|
||||
function arrow () {
|
||||
const X_OFFSET = thisObject.container.frame.width / 2
|
||||
const Y_OFFSET = thisObject.container.frame.height / 2
|
||||
const HEIGHT = 6
|
||||
const WIDTH = 18
|
||||
const LINE_WIDTH = 3
|
||||
const OPACITY = 1
|
||||
let DISTANCE_BETWEEN_ARROWS = 0
|
||||
let ARROW_1_DIRECTION = 0
|
||||
let ARROW_2_DIRECTION = 0
|
||||
|
||||
switch (thisObject.status) {
|
||||
|
||||
case ('TOP'): {
|
||||
ARROW_1_DIRECTION = -1
|
||||
ARROW_2_DIRECTION = -1
|
||||
DISTANCE_BETWEEN_ARROWS = 6
|
||||
break
|
||||
}
|
||||
case ('MIDDLE'): {
|
||||
ARROW_1_DIRECTION = 1
|
||||
ARROW_2_DIRECTION = -1
|
||||
DISTANCE_BETWEEN_ARROWS = 9
|
||||
break
|
||||
}
|
||||
case ('BOTTOM'): {
|
||||
ARROW_1_DIRECTION = 1
|
||||
ARROW_2_DIRECTION = 1
|
||||
DISTANCE_BETWEEN_ARROWS = 6
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
point1 = {
|
||||
x: X_OFFSET - WIDTH / 2,
|
||||
y: Y_OFFSET + HEIGHT / 2 * ARROW_1_DIRECTION - DISTANCE_BETWEEN_ARROWS / 2
|
||||
}
|
||||
|
||||
point2 = {
|
||||
x: X_OFFSET,
|
||||
y: Y_OFFSET - HEIGHT / 2 * ARROW_1_DIRECTION - DISTANCE_BETWEEN_ARROWS / 2
|
||||
}
|
||||
|
||||
point3 = {
|
||||
x: X_OFFSET + WIDTH / 2,
|
||||
y: Y_OFFSET + HEIGHT / 2 * ARROW_1_DIRECTION - DISTANCE_BETWEEN_ARROWS / 2
|
||||
}
|
||||
|
||||
point1 = thisObject.container.frame.frameThisPoint(point1)
|
||||
point2 = thisObject.container.frame.frameThisPoint(point2)
|
||||
point3 = thisObject.container.frame.frameThisPoint(point3)
|
||||
|
||||
point4 = {
|
||||
x: X_OFFSET - WIDTH / 2,
|
||||
y: Y_OFFSET + HEIGHT / 2 * ARROW_2_DIRECTION + DISTANCE_BETWEEN_ARROWS / 2
|
||||
}
|
||||
|
||||
point5 = {
|
||||
x: X_OFFSET,
|
||||
y: Y_OFFSET - HEIGHT / 2 * ARROW_2_DIRECTION + DISTANCE_BETWEEN_ARROWS / 2
|
||||
}
|
||||
|
||||
point6 = {
|
||||
x: X_OFFSET + WIDTH / 2,
|
||||
y: Y_OFFSET + HEIGHT / 2 * ARROW_2_DIRECTION + DISTANCE_BETWEEN_ARROWS / 2
|
||||
}
|
||||
|
||||
point4 = thisObject.container.frame.frameThisPoint(point4)
|
||||
point5 = thisObject.container.frame.frameThisPoint(point5)
|
||||
point6 = thisObject.container.frame.frameThisPoint(point6)
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
browserCanvasContext.moveTo(point1.x, point1.y)
|
||||
browserCanvasContext.lineTo(point2.x, point2.y)
|
||||
browserCanvasContext.lineTo(point3.x, point3.y)
|
||||
|
||||
browserCanvasContext.moveTo(point4.x, point4.y)
|
||||
browserCanvasContext.lineTo(point5.x, point5.y)
|
||||
browserCanvasContext.lineTo(point6.x, point6.y)
|
||||
|
||||
browserCanvasContext.lineWidth = LINE_WIDTH
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + OPACITY + ')'
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,324 @@
|
||||
|
||||
function newDesignSpace () {
|
||||
const MODULE_NAME = 'Strategy Space'
|
||||
let thisObject = {
|
||||
sidePanel: undefined,
|
||||
container: undefined,
|
||||
iconCollection: undefined,
|
||||
iconByUiObjectType: undefined,
|
||||
workspace: undefined,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
makeVisible: makeVisible,
|
||||
makeInvisible: makeInvisible,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
let container = newContainer()
|
||||
container.initialize()
|
||||
thisObject.container = container
|
||||
|
||||
container.isDraggeable = false
|
||||
|
||||
thisObject.iconCollection = new Map()
|
||||
thisObject.iconByUiObjectType = new Map()
|
||||
|
||||
return thisObject
|
||||
|
||||
function initialize () {
|
||||
loadIconCollection()
|
||||
buildIconByUiObjectTypeMap()
|
||||
|
||||
thisObject.workspace = newWorkspace()
|
||||
thisObject.workspace.initialize()
|
||||
}
|
||||
|
||||
function buildIconByUiObjectTypeMap () {
|
||||
/* Take types-icons relationships defined at the schema */
|
||||
for (let i = 0; i < APP_SCHEMA_ARRAY.length; i++) {
|
||||
let nodeDefinition = APP_SCHEMA_ARRAY[i]
|
||||
let iconName = nodeDefinition.icon
|
||||
if (iconName !== undefined) {
|
||||
let icon = thisObject.iconCollection.get(iconName)
|
||||
thisObject.iconByUiObjectType.set(nodeDefinition.type, icon)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function loadIconCollection () {
|
||||
const iconsNames = [
|
||||
'analysis',
|
||||
'analysis-1',
|
||||
'approve',
|
||||
'attach',
|
||||
'attractive',
|
||||
'pyramid',
|
||||
'brainstorming',
|
||||
'broken-link',
|
||||
'chat',
|
||||
'chronometer',
|
||||
'compass',
|
||||
'competition',
|
||||
'content',
|
||||
'design-tool',
|
||||
'grid',
|
||||
'headphones',
|
||||
'html',
|
||||
'image',
|
||||
'layout',
|
||||
'loading',
|
||||
'login',
|
||||
'pantone',
|
||||
'paper-plane',
|
||||
'photo-camera',
|
||||
'piggy-bank',
|
||||
'pipette',
|
||||
'pixel',
|
||||
'placeholder',
|
||||
'planning',
|
||||
'promotion',
|
||||
'pyramid',
|
||||
'quality',
|
||||
'responsive',
|
||||
'schedule',
|
||||
'search',
|
||||
'security',
|
||||
'settings',
|
||||
'sitemap',
|
||||
'startup',
|
||||
'support',
|
||||
'tap',
|
||||
'menu-fix-pinned',
|
||||
'menu-fix-unpinned',
|
||||
'targeting',
|
||||
'task',
|
||||
'testing',
|
||||
'text',
|
||||
'timeline',
|
||||
'tools',
|
||||
'delete',
|
||||
'upload',
|
||||
'vector',
|
||||
'video-player',
|
||||
'menu-mobility-freeze',
|
||||
'menu-mobility-unfreeze',
|
||||
'menu-tree-minus',
|
||||
'menu-tree-plus',
|
||||
'menu-backup',
|
||||
'menu-share',
|
||||
'stop',
|
||||
'stage-open',
|
||||
'stage-close',
|
||||
'stage-trigger',
|
||||
'stage-trigger-trigger-on',
|
||||
'stage-trigger-trigger-off',
|
||||
'stage-trigger-take-position',
|
||||
'menu-tensor-fixed-angles',
|
||||
'menu-tensor-free-angles',
|
||||
'stage-open-position-size',
|
||||
'stage-open-take-profit',
|
||||
'stage-open-postion-rate',
|
||||
'session-live-trading',
|
||||
'session-paper-trading',
|
||||
'session-backtesting',
|
||||
'session-forward-testing',
|
||||
'network',
|
||||
'network-node',
|
||||
'bot-trading',
|
||||
'layer-manager',
|
||||
'process',
|
||||
'bot-indicator',
|
||||
'bot-sensor',
|
||||
'time-period',
|
||||
'data-mine',
|
||||
'procedure-loop',
|
||||
'calculations-procedure',
|
||||
'data-building-procedure',
|
||||
'procedure-initialization',
|
||||
'process-definition',
|
||||
'output-dataset',
|
||||
'data-dependency',
|
||||
'product-definition',
|
||||
'plotter-module',
|
||||
'status-dependency',
|
||||
'record-property',
|
||||
'record-definition',
|
||||
'plotter',
|
||||
'plotter-panel',
|
||||
'dataset-definition',
|
||||
'status-report',
|
||||
'execution-finished-event',
|
||||
'process-dependencies',
|
||||
'process-output',
|
||||
'social-bot',
|
||||
'execution-started-event',
|
||||
'shapes',
|
||||
'chart-point',
|
||||
'style',
|
||||
'chart-points',
|
||||
'shapes-polygon-vertex',
|
||||
'shapes-polygon-border',
|
||||
'shapes-polygon-body',
|
||||
'chart-point-formula',
|
||||
'style-condition',
|
||||
'shapes-polygon',
|
||||
'data-entry',
|
||||
'data-formula',
|
||||
'clone',
|
||||
'exchange-account-asset',
|
||||
'exchange-account-key',
|
||||
'user-keys',
|
||||
'user-assets',
|
||||
'asset',
|
||||
'exchange-accounts',
|
||||
'user-account',
|
||||
'market',
|
||||
'exchange-assets',
|
||||
'crypto-exchanges',
|
||||
'crypto-exchange',
|
||||
'exchange-markets',
|
||||
'charting-space',
|
||||
'time-line-chart',
|
||||
'time-frame-scale',
|
||||
'time-scale',
|
||||
'rate-scale',
|
||||
'time-machine',
|
||||
'data-product',
|
||||
'single-market-data',
|
||||
'data-storage',
|
||||
'dataset',
|
||||
'viewport',
|
||||
'bitfinex',
|
||||
'binance',
|
||||
'kucoin',
|
||||
'bittrex',
|
||||
'kraken',
|
||||
'poloniex',
|
||||
'upbit',
|
||||
'monero',
|
||||
'tether',
|
||||
'tron',
|
||||
'stellar',
|
||||
'qtum',
|
||||
'neo',
|
||||
'litecoin',
|
||||
'ethereum-classic',
|
||||
'ethereum',
|
||||
'eos',
|
||||
'dash',
|
||||
'cardano',
|
||||
'bitcoin-cash',
|
||||
'bitcoin',
|
||||
'binance-coin',
|
||||
'trend',
|
||||
'oscillator',
|
||||
'moving-average',
|
||||
'histogram',
|
||||
'band',
|
||||
'volume',
|
||||
'candles',
|
||||
'bitmex',
|
||||
'liquid',
|
||||
'hitbtc',
|
||||
'us-dollar',
|
||||
'distance-to-parent-200',
|
||||
'distance-to-parent-150',
|
||||
'distance-to-parent-100',
|
||||
'distance-to-parent-050',
|
||||
'distance-to-parent-025',
|
||||
'distance-to-parent-000',
|
||||
'angle-to-parent-360',
|
||||
'angle-to-parent-180',
|
||||
'angle-to-parent-090',
|
||||
'angle-to-parent-045',
|
||||
'angle-to-parent-000',
|
||||
'data-mining',
|
||||
'initial-definition',
|
||||
'next-phase-event',
|
||||
'testing-environment',
|
||||
'production-environment',
|
||||
'task-manager',
|
||||
'session-based-data',
|
||||
'session-independent-data',
|
||||
'session-reference',
|
||||
'dashboard',
|
||||
'toggle-auto-scale-manual',
|
||||
'toggle-auto-scale-auto-min-max',
|
||||
'toggle-auto-scale-auto-max',
|
||||
'toggle-auto-scale-auto-min',
|
||||
'toggle-panel-off',
|
||||
'toggle-auto-time-scale-auto-min',
|
||||
'toggle-auto-time-scale-auto-max',
|
||||
'toggle-auto-time-scale-auto-min-max',
|
||||
'toggle-auto-time-scale-manual',
|
||||
'mouse-pointer',
|
||||
'mouse-left-click',
|
||||
'mouse-right-click',
|
||||
'mouse-wheel-up',
|
||||
'mouse-wheel-down',
|
||||
'mouse-wheel-click',
|
||||
'key-alt',
|
||||
'key-shift',
|
||||
'key-ctrl',
|
||||
'hand-drag',
|
||||
'key-down',
|
||||
'key-left',
|
||||
'key-right',
|
||||
'key-up'
|
||||
]
|
||||
|
||||
for (let i = 0; i < iconsNames.length; i++) {
|
||||
let name = iconsNames[i]
|
||||
loadImage(name)
|
||||
}
|
||||
|
||||
function loadImage (name) {
|
||||
const PATH = 'Images/Icons/style-01/'
|
||||
let image = new Image()
|
||||
image.onload = onImageLoad
|
||||
image.fileName = name
|
||||
|
||||
function onImageLoad () {
|
||||
image.canDrawIcon = true
|
||||
}
|
||||
image.src = window.canvasApp.urlPrefix + PATH + name + '.png'
|
||||
thisObject.iconCollection.set(name, image)
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
thisObject.workspace.physics()
|
||||
}
|
||||
|
||||
function makeVisible () {
|
||||
visible = true
|
||||
}
|
||||
|
||||
function makeInvisible () {
|
||||
visible = false
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
|
||||
if (thisObject.sidePanel !== undefined) {
|
||||
container = thisObject.sidePanel.getContainer(point)
|
||||
if (container !== undefined) {
|
||||
container.space = 'Design Space'
|
||||
return container
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.sidePanel !== undefined) {
|
||||
thisObject.sidePanel.draw()
|
||||
}
|
||||
if (thisObject.workspace !== undefined) {
|
||||
thisObject.workspace.draw()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
|
||||
function newFloatingSpace () {
|
||||
const MODULE_NAME = 'Floating Space'
|
||||
const ERROR_LOG = true
|
||||
/*
|
||||
The Floating Space is the place where floating elements like floatingObjects, live and are rendered.
|
||||
This space has its own physics which helps with the animation of these objects and also preventing them to overlap.
|
||||
*/
|
||||
|
||||
let thisObject = {
|
||||
floatingLayer: undefined, // This is the array of floatingObjects being displayed
|
||||
uiObjectConstructor: undefined,
|
||||
container: undefined,
|
||||
oneScreenUp: oneScreenUp,
|
||||
oneScreenDown: oneScreenDown,
|
||||
oneScreenLeft: oneScreenLeft,
|
||||
oneScreenRight: oneScreenRight,
|
||||
positionAtNode: positionAtNode,
|
||||
fitIntoVisibleArea: fitIntoVisibleArea,
|
||||
isThisPointVisible: isThisPointVisible,
|
||||
isItFar: isItFar,
|
||||
makeVisible: makeVisible,
|
||||
makeInvisible: makeInvisible,
|
||||
draw: draw,
|
||||
physics: physics,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize,
|
||||
finalize: finalize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize(MODULE_NAME)
|
||||
thisObject.container.isClickeable = false
|
||||
thisObject.container.isDraggeable = true
|
||||
thisObject.container.isWheelable = true
|
||||
thisObject.container.detectMouseOver = true
|
||||
thisObject.container.frame.radius = 0
|
||||
|
||||
let devicePixelRatio = window.devicePixelRatio
|
||||
const SPACE_SIZE = 50000
|
||||
|
||||
thisObject.container.frame.width = SPACE_SIZE
|
||||
thisObject.container.frame.height = SPACE_SIZE
|
||||
thisObject.container.frame.position.x = browserCanvas.width / 2 - thisObject.container.frame.width / 2
|
||||
thisObject.container.frame.position.y = browserCanvas.height / 2 - thisObject.container.frame.height / 2
|
||||
|
||||
let visible = false
|
||||
|
||||
const PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT = 25
|
||||
let eventSubscriptionId
|
||||
|
||||
return thisObject
|
||||
|
||||
function finalize () {
|
||||
thisObject.container.eventHandler.stopListening(eventSubscriptionId)
|
||||
|
||||
thisObject.floatingLayer.finalize()
|
||||
thisObject.uiObjectConstructor.finalize()
|
||||
thisObject.container.finalize()
|
||||
|
||||
thisObject.floatingLayer = undefined
|
||||
thisObject.uiObjectConstructor = undefined
|
||||
thisObject.container = undefined
|
||||
}
|
||||
|
||||
function initialize (callBackFunction) {
|
||||
thisObject.floatingLayer = newFloatingLayer()
|
||||
thisObject.floatingLayer.initialize()
|
||||
|
||||
thisObject.uiObjectConstructor = newUiObjectConstructor()
|
||||
thisObject.uiObjectConstructor.initialize(thisObject.floatingLayer)
|
||||
|
||||
eventSubscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseWheel', onMouseWheel)
|
||||
}
|
||||
|
||||
function isItFar (payload, dontCheckParent) {
|
||||
/* If for any reason the paylaod is undefined we return false */
|
||||
if (payload === undefined) { return false }
|
||||
|
||||
let radarFactor = 2 // How big is the margin
|
||||
|
||||
/* If the chain parent is not far, they we dont consither this far. */
|
||||
if (dontCheckParent !== true) {
|
||||
if (payload.chainParent !== undefined) {
|
||||
if (isItFar(payload.chainParent.payload, true) === false) { return false }
|
||||
}
|
||||
}
|
||||
|
||||
/* Exceptions that are never considered far. */
|
||||
if (
|
||||
payload.node.type === 'Trading System' ||
|
||||
payload.node.type === 'Network' ||
|
||||
payload.node.type === 'Crypto Ecosystem' ||
|
||||
payload.node.type === 'Charting Space' ||
|
||||
payload.node.type === 'Data Mine'
|
||||
) {
|
||||
return false
|
||||
}
|
||||
|
||||
/* Another exception are the ones who have reference parents */
|
||||
if (payload.referenceParent !== undefined) { return false }
|
||||
|
||||
/* Here we will check the position of a floatingobject to see if it is outside the screen, with a margin of one screen around. */
|
||||
let point = thisObject.container.frame.frameThisPoint(payload.position)
|
||||
|
||||
if (point.x > browserCanvas.width + browserCanvas.width * radarFactor) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (point.x < 0 - browserCanvas.width * radarFactor) {
|
||||
return true
|
||||
}
|
||||
|
||||
let bottom = COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT
|
||||
let heightDiff = browserCanvas.height - bottom
|
||||
if (point.y < bottom - heightDiff * radarFactor) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (point.y > browserCanvas.height + heightDiff * radarFactor) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
function fitIntoVisibleArea (point) {
|
||||
/* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */
|
||||
|
||||
let returnPoint = {
|
||||
x: point.x,
|
||||
y: point.y
|
||||
}
|
||||
|
||||
if (point.x > browserCanvas.width) {
|
||||
returnPoint.x = browserCanvas.width
|
||||
}
|
||||
|
||||
if (point.x < 0) {
|
||||
returnPoint.x = 0
|
||||
}
|
||||
|
||||
if (point.y < COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT) {
|
||||
returnPoint.y = COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT
|
||||
}
|
||||
|
||||
if (point.y > browserCanvas.height) {
|
||||
returnPoint.y = browserCanvas.height
|
||||
}
|
||||
|
||||
return returnPoint
|
||||
}
|
||||
|
||||
function oneScreenUp () {
|
||||
let displaceVector = {
|
||||
x: 0,
|
||||
y: browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100
|
||||
}
|
||||
|
||||
thisObject.container.displace(displaceVector)
|
||||
return displaceVector
|
||||
}
|
||||
|
||||
function oneScreenDown () {
|
||||
let displaceVector = {
|
||||
x: 0,
|
||||
y: -browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100
|
||||
}
|
||||
|
||||
thisObject.container.displace(displaceVector)
|
||||
return displaceVector
|
||||
}
|
||||
|
||||
function oneScreenLeft () {
|
||||
let displaceVector = {
|
||||
x: browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100,
|
||||
y: 0
|
||||
}
|
||||
|
||||
thisObject.container.displace(displaceVector)
|
||||
return displaceVector
|
||||
}
|
||||
|
||||
function oneScreenRight () {
|
||||
let displaceVector = {
|
||||
x: -browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100,
|
||||
y: 0
|
||||
}
|
||||
|
||||
thisObject.container.displace(displaceVector)
|
||||
return displaceVector
|
||||
}
|
||||
|
||||
function positionAtNode (node) {
|
||||
let position = thisObject.container.frame.frameThisPoint(node.payload.position)
|
||||
|
||||
let displaceVector = {
|
||||
x: browserCanvas.width / 2 - position.x,
|
||||
y: (COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT) + (browserCanvas.height - (COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT)) / 2 - position.y
|
||||
}
|
||||
|
||||
thisObject.container.displace(displaceVector)
|
||||
}
|
||||
|
||||
function isThisPointVisible (point) {
|
||||
if (point.x > browserCanvas.width) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (point.x < 0) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (point.y < COCKPIT_SPACE_POSITION + COCKPIT_SPACE_HEIGHT) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (point.y > browserCanvas.height) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function onMouseWheel (event) {
|
||||
thisObject.floatingLayer.changeTargetRepulsion(event.wheelDelta)
|
||||
}
|
||||
|
||||
function makeVisible () {
|
||||
visible = true
|
||||
}
|
||||
|
||||
function makeInvisible () {
|
||||
visible = false
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
let container
|
||||
|
||||
container = thisObject.floatingLayer.getContainer(point)
|
||||
if (container !== undefined) {
|
||||
container.space = 'Floating Space'
|
||||
return container
|
||||
}
|
||||
|
||||
if (visible === true) {
|
||||
thisObject.container.space = 'Floating Space'
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (visible === true) {
|
||||
browserZoomPhysics()
|
||||
thisObject.floatingLayer.physics()
|
||||
}
|
||||
}
|
||||
|
||||
function browserZoomPhysics () {
|
||||
if (devicePixelRatio !== window.devicePixelRatio) {
|
||||
devicePixelRatio = window.devicePixelRatio
|
||||
|
||||
thisObject.container.frame.position.x = browserCanvas.width / 2 - thisObject.container.frame.width / 2
|
||||
thisObject.container.frame.position.y = browserCanvas.height / 2 - thisObject.container.frame.height / 2
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (visible === true) {
|
||||
drawBackground()
|
||||
thisObject.floatingLayer.draw()
|
||||
}
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
browserCanvasContext.rect(thisObject.container.frame.position.x, thisObject.container.frame.position.y, thisObject.container.frame.width, thisObject.container.frame.height)
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.BLACK + ', 1)'
|
||||
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,353 @@
|
||||
/*
|
||||
|
||||
The Panel Space y the place wehre all panels live, no matter who create them.
|
||||
|
||||
*/
|
||||
|
||||
function newPanelsSpace () {
|
||||
let thisObject = {
|
||||
visible: true,
|
||||
container: undefined,
|
||||
unHide: unHide,
|
||||
hide: hide,
|
||||
createNewPanel: createNewPanel,
|
||||
destroyPanel: destroyPanel,
|
||||
getPanel: getPanel,
|
||||
physics: physics,
|
||||
draw: draw,
|
||||
panels: [],
|
||||
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
let container = newContainer()
|
||||
container.initialize()
|
||||
thisObject.container = container
|
||||
thisObject.container.isDraggeable = false
|
||||
|
||||
container.frame.containerName = 'Panels Space'
|
||||
|
||||
return thisObject
|
||||
|
||||
function initialize () {
|
||||
|
||||
/* The space does not create any Panels for itself, it just sits and waits others need panels. */
|
||||
|
||||
}
|
||||
|
||||
function createNewPanel (pType, pParameters, pOwner, pSession) {
|
||||
let panel
|
||||
|
||||
switch (pType) {
|
||||
|
||||
case 'Layers Panel':
|
||||
{
|
||||
panel = newLayersPanel()
|
||||
panel.fitFunction = canvas.chartingSpace.fitFunction
|
||||
panel.container.isVisibleFunction = canvas.chartingSpace.isThisPointVisible
|
||||
panel.type = 'Layers Panel'
|
||||
break
|
||||
}
|
||||
case 'Plotter Panel':
|
||||
{
|
||||
if (pParameters.panelNode.code.isLegacy !== true) {
|
||||
panel = newPlotterPanel()
|
||||
panel.fitFunction = canvas.chartingSpace.fitFunction
|
||||
panel.container.isVisibleFunction = canvas.chartingSpace.isThisPointVisible
|
||||
panel.session = pSession
|
||||
panel.initialize(pParameters.panelNode)
|
||||
} else {
|
||||
panel = getNewPlotterPanel(pParameters.dataMine, pParameters.plotterCodeName, pParameters.moduleCodeName, pParameters.panelNode.code.codeName)
|
||||
panel.fitFunction = canvas.chartingSpace.fitFunction
|
||||
panel.container.isVisibleFunction = canvas.chartingSpace.isThisPointVisible
|
||||
panel.session = pSession
|
||||
panel.initialize()
|
||||
}
|
||||
panel.type = 'Plotter Panel'
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
panel.owner = pOwner
|
||||
thisObject.panels.push(panel)
|
||||
|
||||
panel.handle = Math.floor((Math.random() * 10000000) + 1)
|
||||
|
||||
return panel.handle
|
||||
}
|
||||
|
||||
function unHide (owner, type) {
|
||||
for (let i = 0; i < thisObject.panels.length; i++) {
|
||||
let panel = thisObject.panels[i]
|
||||
if (panel.owner === owner && panel.type === type) {
|
||||
panel.isHidden = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hide (owner, type) {
|
||||
for (let i = 0; i < thisObject.panels.length; i++) {
|
||||
let panel = thisObject.panels[i]
|
||||
if (panel.owner === owner && panel.type === type) {
|
||||
panel.isHidden = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function destroyPanel (pPanelHandle) {
|
||||
for (let i = 0; i < thisObject.panels.length; i++) {
|
||||
let panel = thisObject.panels[i]
|
||||
|
||||
if (panel.handle === pPanelHandle) {
|
||||
if (panel.finalize !== undefined) {
|
||||
panel.finalize()
|
||||
}
|
||||
|
||||
thisObject.panels.splice(i, 1) // Delete item from array.
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getPanel (pPanelHandle) {
|
||||
for (let i = 0; i < thisObject.panels.length; i++) {
|
||||
let panel = thisObject.panels[i]
|
||||
|
||||
if (panel.handle === pPanelHandle) {
|
||||
return panel
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (thisObject.visible !== true) { return }
|
||||
|
||||
childrenPhysics()
|
||||
positioningPhysics()
|
||||
}
|
||||
|
||||
function childrenPhysics () {
|
||||
if (thisObject.panels !== undefined) {
|
||||
for (let i = 0; i < thisObject.panels.length; i++) {
|
||||
let panel = thisObject.panels[i]
|
||||
let owner = canvas.chartingSpace.inViewport.get(panel.owner)
|
||||
if (owner !== undefined) {
|
||||
if (panel.physics !== undefined) {
|
||||
panel.physics()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function positioningPhysics () {
|
||||
if (thisObject.panels !== undefined) {
|
||||
for (let i = 0; i < thisObject.panels.length; i++) {
|
||||
let panel = thisObject.panels[i]
|
||||
if (panel.isVisible === false || panel.isHidden === true) { continue }
|
||||
/* setting the speed of the panel */
|
||||
if (panel.container.speed === undefined) {
|
||||
panel.container.speed = {
|
||||
x: 2,
|
||||
y: 2
|
||||
}
|
||||
}
|
||||
|
||||
/* Trying to move the panel and see if it is possible */
|
||||
let centerPoint = {
|
||||
x: panel.container.frame.position.x,
|
||||
y: panel.container.frame.position.y
|
||||
}
|
||||
centerPoint.x = centerPoint.x + panel.container.frame.width / 2
|
||||
centerPoint.y = centerPoint.y + panel.container.frame.height / 2
|
||||
|
||||
/* Lets see which quadrant the panel is at */
|
||||
let verticalLine = (canvas.chartingSpace.viewport.visibleArea.topRight.x - canvas.chartingSpace.viewport.visibleArea.topLeft.x) / 2 + canvas.chartingSpace.viewport.visibleArea.topLeft.x
|
||||
let horizontalLine = (canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topRight.y) / 2 + canvas.chartingSpace.viewport.visibleArea.topRight.y
|
||||
|
||||
if (panel.panelTabButton !== undefined) {
|
||||
if (panel.panelTabButton.status === 'up') {
|
||||
if (centerPoint.x < verticalLine) {
|
||||
panel.gravitatesTowards = 'topLeft'
|
||||
} else {
|
||||
panel.gravitatesTowards = 'topRight'
|
||||
}
|
||||
} else {
|
||||
if (centerPoint.x < verticalLine) {
|
||||
panel.gravitatesTowards = 'bottomLeft'
|
||||
} else {
|
||||
panel.gravitatesTowards = 'bottomRight'
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (centerPoint.x < verticalLine && centerPoint.y < horizontalLine) {
|
||||
panel.gravitatesTowards = 'topLeft'
|
||||
}
|
||||
if (centerPoint.x >= verticalLine && centerPoint.y < horizontalLine) {
|
||||
panel.gravitatesTowards = 'topRight'
|
||||
}
|
||||
if (centerPoint.x < verticalLine && centerPoint.y >= horizontalLine) {
|
||||
panel.gravitatesTowards = 'bottomLeft'
|
||||
}
|
||||
if (centerPoint.x >= verticalLine && centerPoint.y >= horizontalLine) {
|
||||
panel.gravitatesTowards = 'bottomRight'
|
||||
}
|
||||
}
|
||||
|
||||
/* According to the quadrant we push the panels to the sides */
|
||||
if (panel.gravitatesTowards === 'topLeft' || panel.gravitatesTowards === 'bottomLeft') {
|
||||
panel.container.frame.position.x = panel.container.frame.position.x - panel.container.speed.x
|
||||
isOverlapping(i, panel.container)
|
||||
if (panel.container.frame.position.x < 0) {
|
||||
panel.container.frame.position.x = 0
|
||||
}
|
||||
} else {
|
||||
panel.container.frame.position.x = panel.container.frame.position.x + panel.container.speed.x
|
||||
isOverlapping(i, panel.container)
|
||||
if (panel.container.frame.position.x + panel.container.frame.width > browserCanvas.width) {
|
||||
panel.container.frame.position.x = browserCanvas.width - panel.container.frame.width
|
||||
}
|
||||
}
|
||||
if (panel.container.frame.height <= canvas.chartingSpace.viewport.visibleArea.bottomRight.y - canvas.chartingSpace.viewport.visibleArea.topRight.y) {
|
||||
if (panel.gravitatesTowards === 'topLeft' || panel.gravitatesTowards === 'topRight') {
|
||||
panel.container.frame.position.y = panel.container.frame.position.y - panel.container.speed.y
|
||||
isOverlapping(i, panel.container)
|
||||
if (panel.container.frame.position.y < canvas.chartingSpace.viewport.visibleArea.topLeft.y) {
|
||||
panel.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.topLeft.y
|
||||
}
|
||||
} else {
|
||||
panel.container.frame.position.y = panel.container.frame.position.y + panel.container.speed.y
|
||||
isOverlapping(i, panel.container)
|
||||
if (panel.container.frame.position.y + panel.container.frame.height > canvas.chartingSpace.viewport.visibleArea.bottomRight.y) {
|
||||
panel.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomRight.y - panel.container.frame.height
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isOverlapping (currentIndex, currentContainer) {
|
||||
let corner1 = {
|
||||
x: currentContainer.frame.position.x,
|
||||
y: currentContainer.frame.position.y
|
||||
}
|
||||
let corner2 = {
|
||||
x: currentContainer.frame.position.x + currentContainer.frame.width,
|
||||
y: currentContainer.frame.position.y
|
||||
}
|
||||
let corner3 = {
|
||||
x: currentContainer.frame.position.x + currentContainer.frame.width,
|
||||
y: currentContainer.frame.position.y + currentContainer.frame.height
|
||||
}
|
||||
let corner4 = {
|
||||
x: currentContainer.frame.position.x,
|
||||
y: currentContainer.frame.position.y + currentContainer.frame.height
|
||||
}
|
||||
|
||||
for (let i = 0; i < currentIndex; i++) {
|
||||
let panel = thisObject.panels[i]
|
||||
if (panel.isVisible === true && panel.isHidden === false) {
|
||||
if (isThisPointInsideThisFrame(corner1, panel.container.frame) === true) {
|
||||
pushOut(currentContainer, panel.container, panel.gravitatesTowards)
|
||||
}
|
||||
if (isThisPointInsideThisFrame(corner2, panel.container.frame) === true) {
|
||||
pushOut(currentContainer, panel.container, panel.gravitatesTowards)
|
||||
}
|
||||
if (isThisPointInsideThisFrame(corner3, panel.container.frame) === true) {
|
||||
pushOut(currentContainer, panel.container, panel.gravitatesTowards)
|
||||
}
|
||||
if (isThisPointInsideThisFrame(corner4, panel.container.frame) === true) {
|
||||
pushOut(currentContainer, panel.container, panel.gravitatesTowards)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pushOut (currentContainer, controlContainer, corner) {
|
||||
switch (corner) {
|
||||
case 'topLeft': {
|
||||
currentContainer.frame.position.x = controlContainer.frame.position.x + controlContainer.frame.width
|
||||
break
|
||||
}
|
||||
case 'topRight': {
|
||||
currentContainer.frame.position.x = controlContainer.frame.position.x - currentContainer.frame.width
|
||||
break
|
||||
}
|
||||
case 'bottomLeft': {
|
||||
currentContainer.frame.position.x = controlContainer.frame.position.x + controlContainer.frame.width
|
||||
break
|
||||
}
|
||||
case 'bottomRight': {
|
||||
currentContainer.frame.position.x = controlContainer.frame.position.x - currentContainer.frame.width
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function isThisPointInsideThisFrame (point, frame) {
|
||||
let corner1 = {
|
||||
x: frame.position.x,
|
||||
y: frame.position.y
|
||||
}
|
||||
let corner2 = {
|
||||
x: frame.position.x + frame.width,
|
||||
y: frame.position.y
|
||||
}
|
||||
let corner3 = {
|
||||
x: frame.position.x + frame.width,
|
||||
y: frame.position.y + frame.height
|
||||
}
|
||||
let corner4 = {
|
||||
x: frame.position.x,
|
||||
y: frame.position.y + frame.height
|
||||
}
|
||||
if (point.x >= corner1.x && point.y >= corner1.y && point.x <= corner3.x && point.y <= corner3.y) {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (thisObject.visible !== true) { return }
|
||||
|
||||
thisObject.container.frame.draw(false, false)
|
||||
|
||||
for (let i = 0; i < thisObject.panels.length; i++) {
|
||||
let panel = thisObject.panels[i]
|
||||
let owner = canvas.chartingSpace.inViewport.get(panel.owner)
|
||||
if (owner !== undefined) {
|
||||
if (panel.isVisible === true && panel.isHidden === false) {
|
||||
panel.draw()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
if (thisObject.visible !== true) { return }
|
||||
|
||||
let container
|
||||
/*
|
||||
We search for the container of panels in the oposite direction than we do it for drawing them,
|
||||
so panels overlapping others are picked firt although they are drawn last.
|
||||
*/
|
||||
for (let i = thisObject.panels.length - 1; i >= 0; i--) {
|
||||
let panel = thisObject.panels[i]
|
||||
let owner = canvas.chartingSpace.inViewport.get(panel.owner)
|
||||
if (owner !== undefined) {
|
||||
if (panel.isVisible === true && panel.isHidden === false) {
|
||||
container = panel.getContainer(point)
|
||||
}
|
||||
}
|
||||
if (container !== undefined) {
|
||||
container.space = 'Panels Space'
|
||||
return container
|
||||
}
|
||||
}
|
||||
thisObject.container.space = 'Panels Space'
|
||||
return thisObject.container
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
|
||||
function newTopSpace () {
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
draw: draw,
|
||||
getContainer: getContainer,
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
thisObject.container = newContainer()
|
||||
thisObject.container.initialize()
|
||||
|
||||
resize()
|
||||
|
||||
thisObject.container.isDraggeable = false
|
||||
let backgroundImage
|
||||
let logoImage
|
||||
return thisObject
|
||||
|
||||
function initialize () {
|
||||
canvas.eventHandler.listenToEvent('Browser Resized', resize)
|
||||
|
||||
backgroundImage = new Image()
|
||||
logoImage = new Image()
|
||||
|
||||
loadImage('superalgos-header-background', backgroundImage)
|
||||
loadImage('superalgos-logo-for-color-background', logoImage)
|
||||
|
||||
function loadImage (name, image) {
|
||||
const PATH = 'Images/'
|
||||
|
||||
image.onload = onImageLoad
|
||||
image.fileName = name
|
||||
|
||||
function onImageLoad () {
|
||||
image.canDraw = true
|
||||
}
|
||||
image.src = window.canvasApp.urlPrefix + PATH + name + '.png'
|
||||
}
|
||||
}
|
||||
|
||||
function resize () {
|
||||
thisObject.container.frame.width = browserCanvas.width
|
||||
thisObject.container.frame.height = TOP_SPACE_HEIGHT
|
||||
|
||||
thisObject.container.frame.position.x = 0
|
||||
try {
|
||||
thisObject.container.frame.position.y = canvas.chartingSpace.viewport.visibleArea.bottomLeft.y
|
||||
} catch (e) { }
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
|
||||
}
|
||||
|
||||
function draw () {
|
||||
thisObject.container.frame.draw(false, false)
|
||||
|
||||
drawBackground()
|
||||
return
|
||||
}
|
||||
|
||||
function drawBackground () {
|
||||
/* Solid Color */
|
||||
let opacity = 1
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.rect(0, 0, thisObject.container.frame.width, thisObject.container.frame.height)
|
||||
if (areWeRecording === true) {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.PATINATED_TURQUOISE + ', ' + opacity + ')'
|
||||
} else {
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.RUSTED_RED + ', ' + opacity + ')'
|
||||
}
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
|
||||
/* Background Image */
|
||||
if (backgroundImage !== undefined) {
|
||||
if (backgroundImage.canDraw === true) {
|
||||
let imagePosition = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
browserCanvasContext.drawImage(
|
||||
backgroundImage, imagePosition.x,
|
||||
imagePosition.y,
|
||||
browserCanvas.width,
|
||||
TOP_SPACE_HEIGHT - 2)
|
||||
}
|
||||
}
|
||||
|
||||
/* Logo Image */
|
||||
if (logoImage !== undefined) {
|
||||
if (logoImage.canDraw === true) {
|
||||
let imagePosition = {
|
||||
x: 10,
|
||||
y: 0
|
||||
}
|
||||
|
||||
browserCanvasContext.drawImage(
|
||||
logoImage, imagePosition.x,
|
||||
imagePosition.y,
|
||||
150,
|
||||
TOP_SPACE_HEIGHT - 2)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
/*
|
||||
|
||||
The Splash Screen is to show some 'loading...' controls while the app is being loaded on the background. After the loading is complete the Splash Screens dissapears for good.
|
||||
|
||||
*/
|
||||
|
||||
function newSplashScreen () {
|
||||
let thisObject = {
|
||||
container: undefined,
|
||||
draw: draw,
|
||||
timeMachines: [],
|
||||
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
||||
initialize: initialize
|
||||
}
|
||||
|
||||
var container = newContainer()
|
||||
container.initialize()
|
||||
thisObject.container = container
|
||||
|
||||
resize()
|
||||
|
||||
container.frame.containerName = 'Splash Screen'
|
||||
|
||||
container.frame.position.x = 0
|
||||
container.frame.position.y = 0
|
||||
|
||||
container.isDraggeable = false
|
||||
|
||||
let fadeOutCounter = 0
|
||||
let opacity = 1
|
||||
|
||||
let logo
|
||||
let canDrawLogo = false
|
||||
|
||||
return thisObject
|
||||
|
||||
function initialize () {
|
||||
logo = new Image()
|
||||
|
||||
logo.onload = onImageLoad
|
||||
|
||||
function onImageLoad () {
|
||||
canDrawLogo = true
|
||||
}
|
||||
|
||||
logo.src = window.canvasApp.urlPrefix + 'Images/superalgos-logo.png'
|
||||
|
||||
canvas.eventHandler.listenToEvent('Browser Resized', resize)
|
||||
}
|
||||
|
||||
function resize () {
|
||||
thisObject.container.frame.width = browserCanvas.width
|
||||
thisObject.container.frame.height = browserCanvas.height
|
||||
}
|
||||
|
||||
function draw () {
|
||||
if (splashScreenNeeded === false) {
|
||||
stepsInitializationCounter = 100
|
||||
canDrawLogo = false
|
||||
|
||||
fadeOutCounter++
|
||||
|
||||
if (fadeOutCounter > 2) {
|
||||
return
|
||||
}
|
||||
opacity = opacity - 1
|
||||
}
|
||||
|
||||
thisObject.container.frame.draw(false, false)
|
||||
|
||||
/* Set the background. */
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.rect(0, 0, thisObject.container.frame.width, thisObject.container.frame.height)
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', ' + opacity + ')'
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
|
||||
if (canDrawLogo === false) { return }
|
||||
|
||||
/* First the AA Logo. */
|
||||
|
||||
let imageHeight = 192 / 2
|
||||
let imageWidth = 720 / 2
|
||||
let yDisplacement = -60
|
||||
let xDisplacement = -60
|
||||
|
||||
let imagePoint = {
|
||||
x: thisObject.container.frame.width * 1 / 2 - imageWidth / 2 + xDisplacement,
|
||||
y: thisObject.container.frame.height * 1 / 2 - imageHeight / 2 + yDisplacement
|
||||
}
|
||||
|
||||
imagePoint = thisObject.container.frame.frameThisPoint(imagePoint)
|
||||
|
||||
browserCanvasContext.drawImage(logo, imagePoint.x, imagePoint.y, imageWidth, imageHeight)
|
||||
|
||||
/* Second the % of Advance. */
|
||||
|
||||
let label = '' + Math.trunc(stepsInitializationCounter) + ' %'
|
||||
stepsInitializationCounter = stepsInitializationCounter + 5.0
|
||||
|
||||
if (stepsInitializationCounter > 99) {
|
||||
splashScreenNeeded = false
|
||||
stepsInitializationCounter = 99
|
||||
}
|
||||
|
||||
let fontSize = 15
|
||||
|
||||
let labelPoint = {
|
||||
x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO - 22,
|
||||
y: thisObject.container.frame.height / 2 + fontSize / 2 + fontSize * 0.1 + 90 + yDisplacement
|
||||
}
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.fillStyle = 'rgba(0, 0, 0, ' + opacity + ')'
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
|
||||
let title
|
||||
let titlePoint
|
||||
|
||||
/* Algo Bots Competitions Sub Title */
|
||||
|
||||
fontSize = 35
|
||||
|
||||
title = 'Beta'
|
||||
|
||||
titlePoint = {
|
||||
x: thisObject.container.frame.width / 2 - title.length / 2 * fontSize * FONT_ASPECT_RATIO - 20,
|
||||
y: thisObject.container.frame.height / 2 + 70 + yDisplacement
|
||||
}
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.font = 'Saira Condensed'
|
||||
browserCanvasContext.fillStyle = 'rgba(0, 0, 0, ' + opacity + ')'
|
||||
browserCanvasContext.fillText(title, titlePoint.x, titlePoint.y)
|
||||
}
|
||||
|
||||
function getContainer (point) {
|
||||
var container
|
||||
|
||||
/* First we check if this point is inside this space. */
|
||||
|
||||
if (thisObject.container.frame.isThisPointHere(point) === true) {
|
||||
/* Now we see which is the inner most container that has it */
|
||||
|
||||
// add validation of inner most containers here.
|
||||
|
||||
/* The point does not belong to any inner container, so we return the current container. */
|
||||
|
||||
return thisObject.container
|
||||
} else {
|
||||
/* This point does not belong to this space. */
|
||||
|
||||
return undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,156 @@
|
||||
|
||||
function newSystemEventHandler () {
|
||||
/* Web Sockets Connection */
|
||||
|
||||
const MODULE_NAME = 'System Event Handler'
|
||||
const ERROR_LOG = true
|
||||
const INFO_LOG = true
|
||||
const logger = newWebDebugLog()
|
||||
logger.fileName = MODULE_NAME
|
||||
|
||||
const WEB_SOCKETS_URL = 'ws://localhost:8080'
|
||||
let WEB_SOCKETS_CONNECTION
|
||||
|
||||
let thisObject = {
|
||||
initialize: initialize,
|
||||
physics: physics,
|
||||
createEventHandler: createEventHandler,
|
||||
deleteEventHandler: deleteEventHandler,
|
||||
listenToEvent: listenToEvent,
|
||||
stopListening: stopListening,
|
||||
raiseEvent: raiseEvent
|
||||
}
|
||||
|
||||
let eventListeners = new Map()
|
||||
let responseWaiters = new Map()
|
||||
|
||||
return thisObject
|
||||
|
||||
function initialize (callBackFunction) {
|
||||
setuptWebSockets(callBackFunction)
|
||||
}
|
||||
|
||||
function sendCommand (command, responseCallBack, eventsCallBack) {
|
||||
if (command.action === 'listenToEvent') {
|
||||
let key
|
||||
if (command.callerId !== undefined) {
|
||||
key = command.eventHandlerName + '-' + command.eventType + '-' + command.callerId
|
||||
} else {
|
||||
key = command.eventHandlerName + '-' + command.eventType
|
||||
}
|
||||
eventListeners.set(key, eventsCallBack)
|
||||
}
|
||||
if (command.callerId && responseCallBack) {
|
||||
responseWaiters.set(command.callerId, responseCallBack)
|
||||
}
|
||||
|
||||
WEB_SOCKETS_CONNECTION.send(JSON.stringify(command))
|
||||
}
|
||||
|
||||
function createEventHandler (eventHandlerName, callerId, responseCallBack) {
|
||||
let eventCommand = {
|
||||
action: 'createEventHandler',
|
||||
eventHandlerName: eventHandlerName,
|
||||
callerId: callerId
|
||||
}
|
||||
sendCommand(eventCommand, responseCallBack)
|
||||
}
|
||||
|
||||
function deleteEventHandler (eventHandlerName, callerId, responseCallBack) {
|
||||
let eventCommand = {
|
||||
action: 'deleteEventHandler',
|
||||
eventHandlerName: eventHandlerName,
|
||||
callerId: callerId
|
||||
}
|
||||
sendCommand(eventCommand, responseCallBack)
|
||||
}
|
||||
|
||||
function listenToEvent (eventHandlerName, eventType, extraData, callerId, responseCallBack, eventsCallBack) {
|
||||
let eventCommand = {
|
||||
action: 'listenToEvent',
|
||||
eventHandlerName: eventHandlerName,
|
||||
eventType: eventType,
|
||||
extraData: extraData,
|
||||
callerId: callerId
|
||||
}
|
||||
sendCommand(eventCommand, responseCallBack, eventsCallBack)
|
||||
}
|
||||
|
||||
function stopListening (eventHandlerName, eventSubscriptionId, callerId, responseCallBack) {
|
||||
let eventCommand = {
|
||||
action: 'stopListening',
|
||||
eventHandlerName: eventHandlerName,
|
||||
eventSubscriptionId: eventSubscriptionId,
|
||||
callerId: callerId
|
||||
}
|
||||
sendCommand(eventCommand, responseCallBack)
|
||||
}
|
||||
|
||||
function raiseEvent (eventHandlerName, eventType, event, callerId, responseCallBack) {
|
||||
let eventCommand = {
|
||||
action: 'raiseEvent',
|
||||
eventHandlerName: eventHandlerName,
|
||||
eventType: eventType,
|
||||
event: event,
|
||||
callerId: callerId
|
||||
}
|
||||
sendCommand(eventCommand, responseCallBack)
|
||||
}
|
||||
|
||||
function physics () {
|
||||
if (WEB_SOCKETS_CONNECTION !== undefined) {
|
||||
if (WEB_SOCKETS_CONNECTION.readyState === 3) { // Connection closed. May happen after computer goes to sleep.
|
||||
setuptWebSockets(onOpen)
|
||||
function onOpen () {
|
||||
if (INFO_LOG === true) { logger.write('[INFO] setuptWebSockets -> Found Web Sockets Connection Closed. Reconnected to WebSockets Server.') }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function setuptWebSockets (callBackFunction) {
|
||||
try {
|
||||
WEB_SOCKETS_CONNECTION = new WebSocket(WEB_SOCKETS_URL)
|
||||
WEB_SOCKETS_CONNECTION.onopen = () => {
|
||||
console.log('Websocket connection opened.')
|
||||
}
|
||||
WEB_SOCKETS_CONNECTION.onerror = error => {
|
||||
console.log('WebSocket error:' + JSON.stringify(error))
|
||||
}
|
||||
WEB_SOCKETS_CONNECTION.onopen = () => {
|
||||
if (callBackFunction !== undefined) {
|
||||
callBackFunction()
|
||||
}
|
||||
}
|
||||
WEB_SOCKETS_CONNECTION.onmessage = e => {
|
||||
// console.log('Websoked Message Received: ' + e.data)
|
||||
|
||||
let message = JSON.parse(e.data)
|
||||
|
||||
if (message.action === 'Event Raised') {
|
||||
let key
|
||||
if (message.callerId !== undefined) {
|
||||
key = message.eventHandlerName + '-' + message.eventType + '-' + message.callerId
|
||||
} else {
|
||||
key = message.eventHandlerName + '-' + message.eventType
|
||||
}
|
||||
let handler = eventListeners.get(key)
|
||||
if (handler) {
|
||||
handler(message)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if (message.action === 'Event Server Response') {
|
||||
let handler = responseWaiters.get(message.callerId)
|
||||
if (handler) {
|
||||
handler(message)
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (ERROR_LOG === true) { logger.write('[ERROR] setuptWebSockets -> err = ' + err.stack) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
function drawContainerBackground (container, color, opacity, fitFunction) {
|
||||
let fromPoint = {
|
||||
x: 0,
|
||||
y: 0
|
||||
}
|
||||
|
||||
let toPoint = {
|
||||
x: container.frame.width,
|
||||
y: container.frame.height
|
||||
}
|
||||
|
||||
fromPoint = transformThisPoint(fromPoint, container)
|
||||
toPoint = transformThisPoint(toPoint, container)
|
||||
|
||||
fromPoint = fitFunction(fromPoint)
|
||||
toPoint = fitFunction(toPoint)
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
browserCanvasContext.rect(fromPoint.x, fromPoint.y, toPoint.x - fromPoint.x, toPoint.y - fromPoint.y)
|
||||
browserCanvasContext.fillStyle = 'rgba(' + color + ', ' + opacity + ')'
|
||||
browserCanvasContext.closePath()
|
||||
browserCanvasContext.fill()
|
||||
}
|
||||
|
||||
function savePropertyAtNodeConfig (payload, propertyName, value) {
|
||||
try {
|
||||
let code = JSON.parse(payload.node.code)
|
||||
code[propertyName] = value
|
||||
payload.node.code = JSON.stringify(code, null, 4)
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function loadPropertyFromNodeConfig (payload, propertyName) {
|
||||
try {
|
||||
let code = JSON.parse(payload.node.code)
|
||||
return code[propertyName]
|
||||
} catch (err) {
|
||||
// we ignore errors here since most likely they will be parsing errors.
|
||||
}
|
||||
}
|
||||
|
||||
function saveFrame (payload, frame) {
|
||||
payload.frame = {
|
||||
position: {
|
||||
x: frame.position.x,
|
||||
y: frame.position.y
|
||||
},
|
||||
width: frame.width,
|
||||
height: frame.height,
|
||||
radius: frame.radius
|
||||
}
|
||||
}
|
||||
|
||||
function loadFrame (payload, frame) {
|
||||
if (payload.node.savedPayload !== undefined) {
|
||||
if (payload.node.savedPayload.frame !== undefined) {
|
||||
frame.position.x = payload.node.savedPayload.frame.position.x
|
||||
frame.position.y = payload.node.savedPayload.frame.position.y
|
||||
frame.width = payload.node.savedPayload.frame.width
|
||||
frame.height = payload.node.savedPayload.frame.height
|
||||
frame.radius = payload.node.savedPayload.frame.radius
|
||||
payload.node.savedPayload.frame = undefined
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function convertTimeFrameToName (pTimeFrame) {
|
||||
for (let i = 0; i < dailyFilePeriods.length; i++) {
|
||||
let period = dailyFilePeriods[i]
|
||||
if (period[0] === pTimeFrame) {
|
||||
return period[1]
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < marketFilesPeriods.length; i++) {
|
||||
let period = marketFilesPeriods[i]
|
||||
if (period[0] === pTimeFrame) {
|
||||
return period[1]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function downloadText (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 downloadCanvas (filename) {
|
||||
let data = browserCanvas.toDataURL('image/png', 1)
|
||||
/* Change MIME type to trick the browser to downlaod the file instead of displaying it */
|
||||
data = data.replace(/^data:image\/[^;]*/, 'data:application/octet-stream')
|
||||
|
||||
/* In addition to <a>'s "download" attribute, you can define HTTP-style headers */
|
||||
data = data.replace(/^data:application\/octet-stream/, 'data:application/octet-stream;headers=Content-Disposition%3A%20attachment%3B%20filename=' + filename + '.png')
|
||||
|
||||
let element = document.createElement('a')
|
||||
element.setAttribute('href', data)
|
||||
element.setAttribute('download', filename + '.PNG')
|
||||
element.style.display = 'none'
|
||||
document.body.appendChild(element)
|
||||
element.click()
|
||||
document.body.removeChild(element)
|
||||
};
|
||||
|
||||
function transformThisPoint (point, container) {
|
||||
/* We make the point relative to the current frame */
|
||||
|
||||
point = container.frame.frameThisPoint(point)
|
||||
|
||||
/* We pass this point through the canvas.chartingSpace.viewport lends, meaning we apply the canvas.chartingSpace.viewport zoom and displacement. */
|
||||
|
||||
point = canvas.chartingSpace.viewport.transformThisPoint(point)
|
||||
|
||||
return point
|
||||
}
|
||||
|
||||
function unTransformThisPoint (point, container) {
|
||||
point = canvas.chartingSpace.viewport.unTransformThisPoint(point)
|
||||
point = container.frame.unframeThisPoint(point)
|
||||
|
||||
return point
|
||||
}
|
||||
|
||||
function nextPorwerOf10 (number) {
|
||||
for (let i = -10; i <= 10; i++) {
|
||||
if (number < Math.pow(10, i)) {
|
||||
return Math.pow(10, i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pad (str, max) {
|
||||
str = str.toString()
|
||||
return str.length < max ? pad('0' + str, max) : str
|
||||
}
|
||||
|
||||
function getDateFromPointAtBrowserCanvas (point, container, coordinateSystem) {
|
||||
point = unTransformThisPoint(point, container)
|
||||
point = coordinateSystem.unInverseTransform(point, container.frame.height)
|
||||
|
||||
let date = new Date(point.x)
|
||||
|
||||
return date
|
||||
}
|
||||
|
||||
function getDateFromPointAtChartsSpace (point, container, coordinateSystem) {
|
||||
point = container.frame.unframeThisPoint(point)
|
||||
point = coordinateSystem.unInverseTransform(point, container.frame.height)
|
||||
|
||||
let date = new Date(point.x)
|
||||
|
||||
return date
|
||||
}
|
||||
|
||||
function getDateFromPointAtContainer (point, container, coordinateSystem) {
|
||||
point = coordinateSystem.unInverseTransform(point, container.frame.height)
|
||||
|
||||
let date = new Date(point.x)
|
||||
|
||||
return date
|
||||
}
|
||||
|
||||
function getRateFromPointAtBrowserCanvas (point, container, coordinateSystem) {
|
||||
point = unTransformThisPoint(point, container)
|
||||
point = coordinateSystem.unInverseTransform(point, container.frame.height)
|
||||
|
||||
return point.y
|
||||
}
|
||||
|
||||
function getRateFromPointAtChartingSpace (point, container, coordinateSystem) {
|
||||
point = container.frame.unframeThisPoint(point)
|
||||
point = coordinateSystem.unInverseTransform(point, container.frame.height)
|
||||
|
||||
return point.y
|
||||
}
|
||||
|
||||
function getRateFromPointAtContainer (point, container, coordinateSystem) {
|
||||
point = coordinateSystem.unInverseTransform(point, container.frame.height)
|
||||
|
||||
return point.y
|
||||
}
|
||||
|
||||
function getMilisecondsFromPoint (point, container, coordinateSystem) {
|
||||
point = unTransformThisPoint(point, container)
|
||||
point = coordinateSystem.unInverseTransform(point, container.frame.height)
|
||||
|
||||
return point.x
|
||||
}
|
||||
|
||||
function moveToUserPosition (container, currentDate, currentRate, coordinateSystem, ignoreX, ignoreY, mousePosition) {
|
||||
let targetPoint = {
|
||||
x: currentDate.valueOf(),
|
||||
y: currentRate
|
||||
}
|
||||
|
||||
/* Put this point in the coordinate system of the canvas.chartingSpace.viewport */
|
||||
targetPoint = coordinateSystem.transformThisPoint(targetPoint)
|
||||
targetPoint = transformThisPoint(targetPoint, container)
|
||||
|
||||
let displaceVector
|
||||
|
||||
let targetNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(targetPoint)
|
||||
let mouseNoZoom = canvas.chartingSpace.viewport.unTransformThisPoint(mousePosition)
|
||||
|
||||
displaceVector = {
|
||||
x: mouseNoZoom.x - targetNoZoom.x,
|
||||
y: mouseNoZoom.y - targetNoZoom.y
|
||||
}
|
||||
|
||||
if (ignoreX) { displaceVector.x = 0 }
|
||||
if (ignoreY) { displaceVector.y = 0 }
|
||||
|
||||
container.displace(displaceVector)
|
||||
}
|
||||
|
||||
function removeTime (datetime) {
|
||||
if (datetime === undefined) { return }
|
||||
|
||||
let dateOnly = new Date(Math.trunc(datetime.valueOf() / ONE_DAY_IN_MILISECONDS) * ONE_DAY_IN_MILISECONDS)
|
||||
|
||||
return dateOnly
|
||||
}
|
||||
|
||||
function printLabel (labelToPrint, x, y, opacity, fontSize, color) {
|
||||
let labelPoint
|
||||
if (color === undefined) { color = UI_COLOR.DARK }
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
let label = '' + labelToPrint
|
||||
let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO
|
||||
browserCanvasContext.fillStyle = 'rgba(' + color + ', ' + opacity + ')'
|
||||
browserCanvasContext.fillText(label, x, y)
|
||||
}
|
||||
|
||||
function newUniqueId () {
|
||||
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
|
||||
var r = Math.random() * 16 | 0, v = c == 'x' ? r : (r & 0x3 | 0x8)
|
||||
return v.toString(16)
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
function roundedCornersBackground (params) {
|
||||
if (params.xOffset === undefined) { params.xOffset = 0 }
|
||||
borderPoint1 = {
|
||||
x: params.cornerRadius + params.xOffset,
|
||||
y: params.cornerRadius
|
||||
}
|
||||
|
||||
borderPoint2 = {
|
||||
x: params.container.frame.width - params.cornerRadius,
|
||||
y: params.cornerRadius
|
||||
}
|
||||
|
||||
borderPoint3 = {
|
||||
x: params.container.frame.width - params.cornerRadius,
|
||||
y: params.container.frame.height - params.cornerRadius
|
||||
}
|
||||
|
||||
borderPoint4 = {
|
||||
x: params.cornerRadius + params.xOffset,
|
||||
y: params.container.frame.height - params.cornerRadius
|
||||
}
|
||||
|
||||
/* Now the transformations. */
|
||||
|
||||
if (params.coordinateSystem !== undefined) { // For instance Candles would use this
|
||||
borderPoint1 = params.coordinateSystem.transformThisPoint(borderPoint1)
|
||||
borderPoint2 = params.coordinateSystem.transformThisPoint(borderPoint2)
|
||||
borderPoint3 = params.coordinateSystem.transformThisPoint(borderPoint3)
|
||||
borderPoint4 = params.coordinateSystem.transformThisPoint(borderPoint4)
|
||||
} else {
|
||||
if (params.outsideViewPort === true) { // If the element is not affected by zoom
|
||||
borderPoint1 = transformThisPoint(borderPoint1, params.container)
|
||||
borderPoint2 = transformThisPoint(borderPoint2, params.container)
|
||||
borderPoint3 = transformThisPoint(borderPoint3, params.container)
|
||||
borderPoint4 = transformThisPoint(borderPoint4, params.container)
|
||||
} else { // For instance Panels use this
|
||||
borderPoint1 = params.container.frame.frameThisPoint(borderPoint1)
|
||||
borderPoint2 = params.container.frame.frameThisPoint(borderPoint2)
|
||||
borderPoint3 = params.container.frame.frameThisPoint(borderPoint3)
|
||||
borderPoint4 = params.container.frame.frameThisPoint(borderPoint4)
|
||||
}
|
||||
}
|
||||
|
||||
if (params.fitFunction !== undefined) {
|
||||
borderPoint1 = params.fitFunction(borderPoint1)
|
||||
borderPoint2 = params.fitFunction(borderPoint2)
|
||||
borderPoint3 = params.fitFunction(borderPoint3)
|
||||
borderPoint4 = params.fitFunction(borderPoint4)
|
||||
}
|
||||
|
||||
browserCanvasContext.setLineDash([0, 0])
|
||||
|
||||
/* Shadow */
|
||||
|
||||
if (params.castShadow === true) {
|
||||
let SHADOW_OFFSET = 2
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
browserCanvasContext.arc(borderPoint1.x + SHADOW_OFFSET, borderPoint1.y + SHADOW_OFFSET, params.cornerRadius, 1.0 * Math.PI, 1.5 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint2.x + SHADOW_OFFSET, borderPoint2.y - params.cornerRadius + SHADOW_OFFSET)
|
||||
browserCanvasContext.arc(borderPoint2.x + SHADOW_OFFSET, borderPoint2.y + SHADOW_OFFSET, params.cornerRadius, 1.5 * Math.PI, 2.0 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint3.x + params.cornerRadius + SHADOW_OFFSET, borderPoint3.y + SHADOW_OFFSET)
|
||||
browserCanvasContext.arc(borderPoint3.x + SHADOW_OFFSET, borderPoint3.y + SHADOW_OFFSET, params.cornerRadius, 0 * Math.PI, 0.5 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint4.x + SHADOW_OFFSET, borderPoint4.y + params.cornerRadius + SHADOW_OFFSET)
|
||||
browserCanvasContext.arc(borderPoint4.x + SHADOW_OFFSET, borderPoint4.y + SHADOW_OFFSET, params.cornerRadius, 0.5 * Math.PI, 1.0 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint1.x - params.cornerRadius + SHADOW_OFFSET, borderPoint1.y + SHADOW_OFFSET)
|
||||
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.lineWidth = 10
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + 0.01 + ')'
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
browserCanvasContext.lineWidth = 5
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + 0.01 + ')'
|
||||
browserCanvasContext.stroke()
|
||||
|
||||
browserCanvasContext.lineWidth = 1
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + UI_COLOR.BLACK + ', ' + 0.15 + ')'
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
|
||||
/* Background */
|
||||
|
||||
browserCanvasContext.beginPath()
|
||||
|
||||
browserCanvasContext.arc(borderPoint1.x, borderPoint1.y, params.cornerRadius, 1.0 * Math.PI, 1.5 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint2.x, borderPoint2.y - params.cornerRadius)
|
||||
browserCanvasContext.arc(borderPoint2.x, borderPoint2.y, params.cornerRadius, 1.5 * Math.PI, 2.0 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint3.x + params.cornerRadius, borderPoint3.y)
|
||||
browserCanvasContext.arc(borderPoint3.x, borderPoint3.y, params.cornerRadius, 0 * Math.PI, 0.5 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint4.x, borderPoint4.y + params.cornerRadius)
|
||||
browserCanvasContext.arc(borderPoint4.x, borderPoint4.y, params.cornerRadius, 0.5 * Math.PI, 1.0 * Math.PI)
|
||||
browserCanvasContext.lineTo(borderPoint1.x - params.cornerRadius, borderPoint1.y)
|
||||
|
||||
browserCanvasContext.closePath()
|
||||
|
||||
browserCanvasContext.fillStyle = 'rgba(' + params.backgroundColor + ', ' + params.opacity + ')'
|
||||
|
||||
browserCanvasContext.fill()
|
||||
|
||||
browserCanvasContext.lineWidth = params.lineWidth
|
||||
browserCanvasContext.strokeStyle = 'rgba(' + params.borderColor + ', ' + params.opacity + ')'
|
||||
browserCanvasContext.stroke()
|
||||
}
|
||||
|
||||
function drawLabel (label, xFactor, yFactor, xOffset, yOffset, fontSize, container) {
|
||||
if (label === undefined) { return }
|
||||
let fontOffset = label.length * fontSize * FONT_ASPECT_RATIO + 10
|
||||
|
||||
let labelPoint = {
|
||||
x: container.frame.width * xFactor + xOffset - fontOffset / 2,
|
||||
y: container.frame.height * yFactor + yOffset
|
||||
}
|
||||
|
||||
labelPoint = container.frame.frameThisPoint(labelPoint)
|
||||
|
||||
browserCanvasContext.font = fontSize + 'px ' + UI_FONT.PRIMARY
|
||||
browserCanvasContext.fillStyle = 'rgba(' + UI_COLOR.WHITE + ', 1)'
|
||||
|
||||
browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y)
|
||||
}
|
||||
|
||||
function drawIcon (icon, xFactor, yFactor, xOffset, yOffset, imageSize, container) {
|
||||
if (icon !== undefined) {
|
||||
if (icon.canDrawIcon === true) {
|
||||
let imagePosition = {
|
||||
x: container.frame.width * xFactor + xOffset - imageSize / 2,
|
||||
y: container.frame.height * yFactor + yOffset - imageSize / 2
|
||||
}
|
||||
|
||||
imagePosition = container.frame.frameThisPoint(imagePosition)
|
||||
|
||||
browserCanvasContext.drawImage(
|
||||
icon, imagePosition.x,
|
||||
imagePosition.y,
|
||||
imageSize,
|
||||
imageSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
function newVideoRecorder () {
|
||||
let thisObject = {
|
||||
recordCanvas: recordCanvas
|
||||
}
|
||||
let lastAction
|
||||
return thisObject
|
||||
|
||||
function recordCanvas () {
|
||||
{
|
||||
const DISTANCE_BETWEEN_ICONS = 35
|
||||
const ICON_SIZES = 50
|
||||
|
||||
let mousePointerIcon = canvas.designSpace.iconCollection.get('mouse-pointer')
|
||||
let draggingIcon = canvas.designSpace.iconCollection.get('hand-drag')
|
||||
|
||||
let leftClickIcon = canvas.designSpace.iconCollection.get('mouse-left-click')
|
||||
let rightClickIcon = canvas.designSpace.iconCollection.get('mouse-right-click')
|
||||
let wheelClickIcon = canvas.designSpace.iconCollection.get('mouse-wheel-click')
|
||||
|
||||
let imagePosition
|
||||
let buttonPressedIcon
|
||||
|
||||
if (canvas.mouse.action === 'key down' && lastAction === 'dragging') {
|
||||
canvas.mouse.action = 'dragging'
|
||||
}
|
||||
switch (canvas.mouse.action) {
|
||||
case 'moving': {
|
||||
pointerIcon = mousePointerIcon
|
||||
break
|
||||
}
|
||||
case 'dragging': {
|
||||
pointerIcon = draggingIcon
|
||||
break
|
||||
}
|
||||
case 'wheel': {
|
||||
if (canvas.mouse.event.delta > 0) {
|
||||
pointerIcon = canvas.designSpace.iconCollection.get('mouse-wheel-up')
|
||||
} else {
|
||||
pointerIcon = canvas.designSpace.iconCollection.get('mouse-wheel-down')
|
||||
}
|
||||
|
||||
break
|
||||
}
|
||||
case 'key down': {
|
||||
pointerIcon = mousePointerIcon
|
||||
break
|
||||
}
|
||||
case 'key up': {
|
||||
pointerIcon = mousePointerIcon
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
lastAction = canvas.mouse.action
|
||||
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x,
|
||||
y: canvas.mouse.position.y
|
||||
}
|
||||
drawMousePointer(pointerIcon, imagePosition, ICON_SIZES)
|
||||
|
||||
switch (canvas.mouse.event.buttons) {
|
||||
case 0: {
|
||||
buttonPressedIcon = undefined
|
||||
break
|
||||
}
|
||||
case 1: {
|
||||
buttonPressedIcon = leftClickIcon
|
||||
break
|
||||
}
|
||||
case 2: {
|
||||
buttonPressedIcon = rightClickIcon
|
||||
break
|
||||
}
|
||||
case 3: {
|
||||
buttonPressedIcon = undefined
|
||||
break
|
||||
}
|
||||
case 4: {
|
||||
buttonPressedIcon = wheelClickIcon
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let firstRow = DISTANCE_BETWEEN_ICONS * 1 + ICON_SIZES * 1
|
||||
let secondRow = DISTANCE_BETWEEN_ICONS * 1 + ICON_SIZES * 2
|
||||
let icon
|
||||
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x + DISTANCE_BETWEEN_ICONS * 2,
|
||||
y: canvas.mouse.position.y
|
||||
}
|
||||
drawMousePointer(buttonPressedIcon, imagePosition, ICON_SIZES)
|
||||
|
||||
if (canvas.mouse.event.shiftKey === true) {
|
||||
icon = canvas.designSpace.iconCollection.get('key-shift')
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x - DISTANCE_BETWEEN_ICONS * 3,
|
||||
y: canvas.mouse.position.y + firstRow
|
||||
}
|
||||
drawMousePointer(icon, imagePosition, ICON_SIZES)
|
||||
}
|
||||
|
||||
if (canvas.mouse.event.ctrlKey === true || canvas.mouse.event.metaKey === true) {
|
||||
icon = canvas.designSpace.iconCollection.get('key-ctrl')
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x - DISTANCE_BETWEEN_ICONS * 3,
|
||||
y: canvas.mouse.position.y + secondRow
|
||||
}
|
||||
drawMousePointer(icon, imagePosition, ICON_SIZES)
|
||||
}
|
||||
|
||||
if (canvas.mouse.event.altKey === true) {
|
||||
icon = canvas.designSpace.iconCollection.get('key-alt')
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x + DISTANCE_BETWEEN_ICONS * 3 + ICON_SIZES * 0,
|
||||
y: canvas.mouse.position.y + secondRow
|
||||
}
|
||||
drawMousePointer(icon, imagePosition, ICON_SIZES)
|
||||
}
|
||||
|
||||
if (canvas.mouse.event.code === 'ArrowUp') {
|
||||
icon = canvas.designSpace.iconCollection.get('key-up')
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x - 0,
|
||||
y: canvas.mouse.position.y + firstRow
|
||||
}
|
||||
drawMousePointer(icon, imagePosition, ICON_SIZES)
|
||||
}
|
||||
|
||||
if (canvas.mouse.event.code === 'ArrowDown') {
|
||||
icon = canvas.designSpace.iconCollection.get('key-down')
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x - 0,
|
||||
y: canvas.mouse.position.y + secondRow
|
||||
}
|
||||
drawMousePointer(icon, imagePosition, ICON_SIZES)
|
||||
}
|
||||
|
||||
if (canvas.mouse.event.code === 'ArrowLeft') {
|
||||
icon = canvas.designSpace.iconCollection.get('key-left')
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x - DISTANCE_BETWEEN_ICONS * 0 - ICON_SIZES * 1,
|
||||
y: canvas.mouse.position.y + secondRow
|
||||
}
|
||||
drawMousePointer(icon, imagePosition, ICON_SIZES)
|
||||
}
|
||||
|
||||
if (canvas.mouse.event.code === 'ArrowRight') {
|
||||
icon = canvas.designSpace.iconCollection.get('key-right')
|
||||
imagePosition = {
|
||||
x: canvas.mouse.position.x + DISTANCE_BETWEEN_ICONS * 0 + ICON_SIZES * 1,
|
||||
y: canvas.mouse.position.y + secondRow
|
||||
}
|
||||
drawMousePointer(icon, imagePosition, ICON_SIZES)
|
||||
}
|
||||
|
||||
function drawMousePointer (icon, imagePosition, imageSize) {
|
||||
if (icon !== undefined) {
|
||||
if (icon.canDrawIcon === true) {
|
||||
browserCanvasContext.drawImage(
|
||||
icon, imagePosition.x,
|
||||
imagePosition.y,
|
||||
imageSize,
|
||||
imageSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
mediaRecorder.capture(browserCanvas)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
function newWebDebugLog () {
|
||||
try {
|
||||
let thisObject = {
|
||||
write: write,
|
||||
fileName: 'undefined'
|
||||
}
|
||||
|
||||
return thisObject
|
||||
|
||||
function write (pText) {
|
||||
console.log(spacePad(thisObject.fileName, 50) + ' : ' + pText)
|
||||
|
||||
function spacePad (str, max) {
|
||||
str = str.toString()
|
||||
return str.length < max ? spacePad(' ' + str, max) : str
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(spacePad(MODULE_NAME, 50) + ' : ' + '[ERROR] WebDebugLog --> err = ' + err.stack)
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
function Stack(){
|
||||
Object.defineProperties(
|
||||
this,
|
||||
{
|
||||
add:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:addToStack
|
||||
},
|
||||
next:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:run
|
||||
},
|
||||
clear:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:clearStack
|
||||
},
|
||||
contents:{
|
||||
enumerable:false,
|
||||
get:getStack,
|
||||
set:setStack
|
||||
},
|
||||
autoRun:{
|
||||
enumerable:true,
|
||||
writable:true,
|
||||
value:true
|
||||
},
|
||||
stop:{
|
||||
enumerable:true,
|
||||
writable:true,
|
||||
value:false
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var stack=[];
|
||||
var running=false;
|
||||
var stop=false;
|
||||
|
||||
function clearStack(){
|
||||
stack=[];
|
||||
return stack;
|
||||
}
|
||||
|
||||
function getStack(){
|
||||
return stack;
|
||||
}
|
||||
|
||||
function setStack(val){
|
||||
stack=val;
|
||||
return stack;
|
||||
}
|
||||
|
||||
function addToStack(){
|
||||
for(var i in arguments){
|
||||
stack.unshift(arguments[i]);
|
||||
}
|
||||
if(!running && !this.stop && this.autoRun){
|
||||
this.next();
|
||||
}
|
||||
}
|
||||
|
||||
function run(){
|
||||
running=true;
|
||||
if(stack.length<1 || this.stop){
|
||||
running=false;
|
||||
return;
|
||||
}
|
||||
|
||||
stack.shift().bind(this)();
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
var Stack=require('../stack');
|
||||
//create a new Stack instance
|
||||
var stack=new Stack;
|
||||
stack.autoRun=false;
|
||||
|
||||
for(var i=0; i<50; i++){
|
||||
//add a bunch of stuff to the stack
|
||||
stack.add(makeRequest.bind(stack,i));
|
||||
}
|
||||
|
||||
stack.next();
|
||||
|
||||
function makeRequest(index){
|
||||
//do stuff
|
||||
console.log(`making LIFO request ${index}`);
|
||||
|
||||
this.next();
|
||||
}
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"_from": "easy-stack@^1.0.0",
|
||||
"_id": "easy-stack@1.0.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-EskbMIWjfwuqM26UhurEv5Tj54g=",
|
||||
"_location": "/easy-stack",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "range",
|
||||
"registry": true,
|
||||
"raw": "easy-stack@^1.0.0",
|
||||
"name": "easy-stack",
|
||||
"escapedName": "easy-stack",
|
||||
"rawSpec": "^1.0.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "^1.0.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/js-queue"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.0.tgz",
|
||||
"_shasum": "12c91b3085a37f0baa336e9486eac4bf94e3e788",
|
||||
"_spec": "easy-stack@^1.0.0",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server\\node_modules\\js-queue",
|
||||
"author": {
|
||||
"name": "Brandon Nozaki Miller"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/RIAEvangelist/easy-stack/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "Simple JS stack with auto run for node and browsers",
|
||||
"engines": {
|
||||
"node": ">=6.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/RIAEvangelist/easy-stack#readme",
|
||||
"keywords": [
|
||||
"stack",
|
||||
"node",
|
||||
"js",
|
||||
"auto",
|
||||
"run",
|
||||
"execute",
|
||||
"browser",
|
||||
"react"
|
||||
],
|
||||
"license": "DBAD",
|
||||
"main": "stack.js",
|
||||
"name": "easy-stack",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/RIAEvangelist/easy-stack.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "1.0.0"
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
class Stack{
|
||||
constructor(){
|
||||
this.stack=[];
|
||||
this.autoRun=true;
|
||||
this.running=false;
|
||||
this.stop=false;
|
||||
}
|
||||
|
||||
clear(){
|
||||
this.stack=[];
|
||||
return this.stack;
|
||||
}
|
||||
|
||||
contents(val){
|
||||
if(val){
|
||||
this.stack=val;
|
||||
}
|
||||
return this.stack;
|
||||
}
|
||||
|
||||
add(...callbacks){
|
||||
this.stack.push(...callbacks);
|
||||
if(!this.running && !this.stop && this.autoRun){
|
||||
this.next();
|
||||
}
|
||||
}
|
||||
|
||||
next(){
|
||||
this.running=true;
|
||||
if(this.stack.length<1 || this.stop){
|
||||
this.running=false;
|
||||
return;
|
||||
}
|
||||
|
||||
this.stack.pop().bind(this)();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports=Stack;
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "event-pubsub",
|
||||
"description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!",
|
||||
"main": "event-pubsub.js",
|
||||
"authors": [
|
||||
"Brandon Nozaki Miller"
|
||||
],
|
||||
"license": "DBAD",
|
||||
"keywords": [
|
||||
"event",
|
||||
"events",
|
||||
"pubsub",
|
||||
"node",
|
||||
"browser"
|
||||
],
|
||||
"homepage": "https://github.com/RIAEvangelist/event-pubsub",
|
||||
"moduleType": [
|
||||
"globals",
|
||||
"node"
|
||||
],
|
||||
"ignore": [
|
||||
"**/.*",
|
||||
"node_modules",
|
||||
"bower_components",
|
||||
"test",
|
||||
"tests"
|
||||
]
|
||||
}
|
||||
+143
@@ -0,0 +1,143 @@
|
||||
'use strict';
|
||||
|
||||
function EventPubSub() {
|
||||
this._events_={};
|
||||
this.publish=this.trigger=this.emit=emit;
|
||||
this.subscribe=this.on=on;
|
||||
this.once=once;
|
||||
this.unSubscribe=this.off=off;
|
||||
this.emit$=emit$;
|
||||
|
||||
function on(type,handler,once){
|
||||
if(!handler){
|
||||
throw new ReferenceError('handler not defined.');
|
||||
}
|
||||
|
||||
if(!this._events_[type]){
|
||||
this._events_[type]=[];
|
||||
}
|
||||
|
||||
if(once){
|
||||
handler._once_ = once;
|
||||
}
|
||||
this._events_[type].push(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
function once(type,handler){
|
||||
return this.on(type, handler, true);
|
||||
}
|
||||
|
||||
function off(type,handler){
|
||||
if(!this._events_[type]){
|
||||
return this;
|
||||
}
|
||||
|
||||
if(!handler){
|
||||
throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler');
|
||||
}
|
||||
|
||||
if(handler=='*'){
|
||||
delete this._events_[type];
|
||||
return this;
|
||||
}
|
||||
|
||||
var handlers=this._events_[type];
|
||||
|
||||
while(handlers.includes(handler)){
|
||||
handlers.splice(
|
||||
handlers.indexOf(handler),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
if(handlers.length<1){
|
||||
delete this._events_[type];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
function emit(type){
|
||||
this.emit$.apply(this, arguments);
|
||||
if(!this._events_[type]){
|
||||
return this;
|
||||
}
|
||||
arguments.splice=Array.prototype.splice;
|
||||
arguments.splice(0,1);
|
||||
|
||||
var handlers=this._events_[type];
|
||||
var onceHandled=[];
|
||||
|
||||
for(var i in handlers){
|
||||
var handler=handlers[i];
|
||||
handler.apply(this, arguments);
|
||||
if(handler._once_){
|
||||
onceHandled.push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
for(var i in onceHandled){
|
||||
this.off(
|
||||
type,
|
||||
onceHandled[i]
|
||||
);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
function emit$(type, args){
|
||||
if(!this._events_['*']){
|
||||
return this;
|
||||
}
|
||||
|
||||
var catchAll=this._events_['*'];
|
||||
|
||||
args.shift=Array.prototype.shift;
|
||||
args.shift(type);
|
||||
|
||||
for(var handler of catchAll){
|
||||
handler.apply(this, args);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!Array.prototype.includes) {
|
||||
Array.prototype.includes = function(searchElement /*, fromIndex*/) {
|
||||
'use strict';
|
||||
if (this == null) {
|
||||
throw new TypeError('Array.prototype.includes called on null or undefined');
|
||||
}
|
||||
|
||||
var O = Object(this);
|
||||
var len = parseInt(O.length, 10) || 0;
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
var n = parseInt(arguments[1], 10) || 0;
|
||||
var k;
|
||||
if (n >= 0) {
|
||||
k = n;
|
||||
} else {
|
||||
k = len + n;
|
||||
if (k < 0) {k = 0;}
|
||||
}
|
||||
var currentElement;
|
||||
while (k < len) {
|
||||
currentElement = O[k];
|
||||
if (searchElement === currentElement ||
|
||||
(searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN
|
||||
return true;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
|
||||
module.exports=EventPubSub;
|
||||
+99
@@ -0,0 +1,99 @@
|
||||
'use strict';
|
||||
|
||||
class EventPubSub {
|
||||
constructor( scope ) {
|
||||
this._events_ = {};
|
||||
this.publish = this.trigger = this.emit;
|
||||
this.subscribe = this.on;
|
||||
this.unSubscribe = this.off;
|
||||
}
|
||||
|
||||
on( type, handler, once ) {
|
||||
if ( !handler ) {
|
||||
throw new ReferenceError( 'handler not defined.' );
|
||||
}
|
||||
|
||||
if ( !this._events_[ type ] ) {
|
||||
this._events_[ type ] = [];
|
||||
}
|
||||
|
||||
if(once){
|
||||
handler._once_ = once;
|
||||
}
|
||||
|
||||
this._events_[ type ].push( handler );
|
||||
return this;
|
||||
}
|
||||
|
||||
once( type, handler ) {
|
||||
return this.on( type, handler, true );
|
||||
}
|
||||
|
||||
off( type, handler ) {
|
||||
if ( !this._events_[ type ] ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if ( !handler ) {
|
||||
throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' );
|
||||
}
|
||||
|
||||
if ( handler == '*' ) {
|
||||
delete this._events_[ type ];
|
||||
return this;
|
||||
}
|
||||
|
||||
const handlers = this._events_[ type ];
|
||||
|
||||
while ( handlers.includes( handler ) ) {
|
||||
handlers.splice(
|
||||
handlers.indexOf( handler ),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
if ( handlers.length < 1 ) {
|
||||
delete this._events_[ type ];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
emit( type, ...args ) {
|
||||
if ( !this._events_[ type ] ) {
|
||||
return this.emit$( type, ...args );
|
||||
}
|
||||
|
||||
const handlers = this._events_[ type ];
|
||||
const onceHandled=[];
|
||||
|
||||
for ( let handler of handlers ) {
|
||||
handler.apply( this, args );
|
||||
if(handler._once_){
|
||||
onceHandled.push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
for(let handler of onceHandled){
|
||||
this.off(type,handler);
|
||||
}
|
||||
|
||||
return this.emit$( type, ...args );
|
||||
}
|
||||
|
||||
emit$( type, ...args ) {
|
||||
if ( !this._events_[ '*' ] ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const catchAll = this._events_[ '*' ];
|
||||
|
||||
for ( let handler of catchAll ) {
|
||||
handler.call( this, type, ...args );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EventPubSub;
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
'use strict';
|
||||
|
||||
window.EventPubSub=function EventPubSub() {
|
||||
this._events_={};
|
||||
this.publish=this.trigger=this.emit=emit;
|
||||
this.subscribe=this.on=on;
|
||||
this.once=once;
|
||||
this.unSubscribe=this.off=off;
|
||||
this.emit$=emit$;
|
||||
|
||||
function on(type,handler,once){
|
||||
if(!handler){
|
||||
throw new ReferenceError('handler not defined.');
|
||||
}
|
||||
|
||||
if(!this._events_[type]){
|
||||
this._events_[type]=[];
|
||||
}
|
||||
|
||||
if(once){
|
||||
handler._once_ = once;
|
||||
}
|
||||
this._events_[type].push(handler);
|
||||
return this;
|
||||
}
|
||||
|
||||
function once(type,handler){
|
||||
return this.on(type, handler, true);
|
||||
}
|
||||
|
||||
function off(type,handler){
|
||||
if(!this._events_[type]){
|
||||
return this;
|
||||
}
|
||||
|
||||
if(!handler){
|
||||
throw new ReferenceError('handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler');
|
||||
}
|
||||
|
||||
if(handler=='*'){
|
||||
delete this._events_[type];
|
||||
return this;
|
||||
}
|
||||
|
||||
var handlers=this._events_[type];
|
||||
|
||||
while(handlers.includes(handler)){
|
||||
handlers.splice(
|
||||
handlers.indexOf(handler),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
if(handlers.length<1){
|
||||
delete this._events_[type];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
function emit(type){
|
||||
this.emit$.apply(this, arguments);
|
||||
if(!this._events_[type]){
|
||||
return this;
|
||||
}
|
||||
arguments.splice=Array.prototype.splice;
|
||||
arguments.splice(0,1);
|
||||
|
||||
var handlers=this._events_[type];
|
||||
var onceHandled=[];
|
||||
|
||||
for(var i in handlers){
|
||||
var handler=handlers[i];
|
||||
handler.apply(this, arguments);
|
||||
if(handler._once_){
|
||||
onceHandled.push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
for(var i in onceHandled){
|
||||
this.off(
|
||||
type,
|
||||
onceHandled[i]
|
||||
);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
function emit$(type, args){
|
||||
if(!this._events_['*']){
|
||||
return this;
|
||||
}
|
||||
|
||||
var catchAll=this._events_['*'];
|
||||
|
||||
args.shift=Array.prototype.shift;
|
||||
args.shift(type);
|
||||
|
||||
for(var handler of catchAll){
|
||||
handler.apply(this, args);
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
if (!Array.prototype.includes) {
|
||||
Array.prototype.includes = function(searchElement /*, fromIndex*/) {
|
||||
'use strict';
|
||||
if (this == null) {
|
||||
throw new TypeError('Array.prototype.includes called on null or undefined');
|
||||
}
|
||||
|
||||
var O = Object(this);
|
||||
var len = parseInt(O.length, 10) || 0;
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
var n = parseInt(arguments[1], 10) || 0;
|
||||
var k;
|
||||
if (n >= 0) {
|
||||
k = n;
|
||||
} else {
|
||||
k = len + n;
|
||||
if (k < 0) {k = 0;}
|
||||
}
|
||||
var currentElement;
|
||||
while (k < len) {
|
||||
currentElement = O[k];
|
||||
if (searchElement === currentElement ||
|
||||
(searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN
|
||||
return true;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
+130
@@ -0,0 +1,130 @@
|
||||
'use strict';
|
||||
|
||||
window.EventPubSub=class EventPubSub {
|
||||
constructor( scope ) {
|
||||
this._events_ = {};
|
||||
this.publish = this.trigger = this.emit;
|
||||
this.subscribe = this.on;
|
||||
this.unSubscribe = this.off;
|
||||
}
|
||||
|
||||
on( type, handler, once ) {
|
||||
if ( !handler ) {
|
||||
throw new ReferenceError( 'handler not defined.' );
|
||||
}
|
||||
|
||||
if ( !this._events_[ type ] ) {
|
||||
this._events_[ type ] = [];
|
||||
}
|
||||
|
||||
if(once){
|
||||
handler._once_ = once;
|
||||
}
|
||||
|
||||
this._events_[ type ].push( handler );
|
||||
return this;
|
||||
}
|
||||
|
||||
once( type, handler ) {
|
||||
return this.on( type, handler, true );
|
||||
}
|
||||
|
||||
off( type, handler ) {
|
||||
if ( !this._events_[ type ] ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
if ( !handler ) {
|
||||
throw new ReferenceError( 'handler not defined. if you wish to remove all handlers from the event please pass "*" as the handler' );
|
||||
}
|
||||
|
||||
if ( handler == '*' ) {
|
||||
delete this._events_[ type ];
|
||||
return this;
|
||||
}
|
||||
|
||||
const handlers = this._events_[ type ];
|
||||
|
||||
while ( handlers.includes( handler ) ) {
|
||||
handlers.splice(
|
||||
handlers.indexOf( handler ),
|
||||
1
|
||||
);
|
||||
}
|
||||
|
||||
if ( handlers.length < 1 ) {
|
||||
delete this._events_[ type ];
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
emit( type, ...args ) {
|
||||
if ( !this._events_[ type ] ) {
|
||||
return this.emit$( type, ...args );
|
||||
}
|
||||
|
||||
const handlers = this._events_[ type ];
|
||||
const onceHandled=[];
|
||||
|
||||
for ( let handler of handlers ) {
|
||||
handler.apply( this, args );
|
||||
if(handler._once_){
|
||||
onceHandled.push(handler);
|
||||
}
|
||||
}
|
||||
|
||||
for(let handler of onceHandled){
|
||||
this.off(type,handler);
|
||||
}
|
||||
|
||||
return this.emit$( type, ...args );
|
||||
}
|
||||
|
||||
emit$( type, ...args ) {
|
||||
if ( !this._events_[ '*' ] ) {
|
||||
return this;
|
||||
}
|
||||
|
||||
const catchAll = this._events_[ '*' ];
|
||||
|
||||
for ( let handler of catchAll ) {
|
||||
handler.call( this, type, ...args );
|
||||
}
|
||||
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
if (!Array.prototype.includes) {
|
||||
Array.prototype.includes = function(searchElement /*, fromIndex*/) {
|
||||
'use strict';
|
||||
if (this == null) {
|
||||
throw new TypeError('Array.prototype.includes called on null or undefined');
|
||||
}
|
||||
|
||||
var O = Object(this);
|
||||
var len = parseInt(O.length, 10) || 0;
|
||||
if (len === 0) {
|
||||
return false;
|
||||
}
|
||||
var n = parseInt(arguments[1], 10) || 0;
|
||||
var k;
|
||||
if (n >= 0) {
|
||||
k = n;
|
||||
} else {
|
||||
k = len + n;
|
||||
if (k < 0) {k = 0;}
|
||||
}
|
||||
var currentElement;
|
||||
while (k < len) {
|
||||
currentElement = O[k];
|
||||
if (searchElement === currentElement ||
|
||||
(searchElement !== searchElement && currentElement !== currentElement)) { // NaN !== NaN
|
||||
return true;
|
||||
}
|
||||
k++;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
}
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
'use strict';
|
||||
|
||||
let EventPubSub = require('./es5');
|
||||
if(process.version[1]>5){
|
||||
EventPubSub = require('./es6');
|
||||
}
|
||||
|
||||
module.exports=EventPubSub;
|
||||
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"_from": "event-pubsub@4.3.0",
|
||||
"_id": "event-pubsub@4.3.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha512-z7IyloorXvKbFx9Bpie2+vMJKKx1fH1EN5yiTfp8CiLOTptSYy1g8H4yDpGlEdshL1PBiFtBHepF2cNsqeEeFQ==",
|
||||
"_location": "/event-pubsub",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "event-pubsub@4.3.0",
|
||||
"name": "event-pubsub",
|
||||
"escapedName": "event-pubsub",
|
||||
"rawSpec": "4.3.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "4.3.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/node-ipc"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/event-pubsub/-/event-pubsub-4.3.0.tgz",
|
||||
"_shasum": "f68d816bc29f1ec02c539dc58c8dd40ce72cb36e",
|
||||
"_spec": "event-pubsub@4.3.0",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server\\node_modules\\node-ipc",
|
||||
"author": {
|
||||
"name": "Brandon Nozaki Miller"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/RIAEvangelist/event-pubsub/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"deprecated": false,
|
||||
"description": "Super light and fast Extensible PubSub events and EventEmitters for Node and the browser with support for ES6 by default, and ES5 versions for older verions of node and older IE/Safari versions. Easy for any developer level. No frills, just high speed pubsub events!",
|
||||
"directories": {
|
||||
"example": "examples"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=4.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/RIAEvangelist/event-pubsub",
|
||||
"keywords": [
|
||||
"event",
|
||||
"events",
|
||||
"pubsub",
|
||||
"node",
|
||||
"browser"
|
||||
],
|
||||
"license": "Unlicense",
|
||||
"main": "event-pubsub.js",
|
||||
"name": "event-pubsub",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/RIAEvangelist/event-pubsub.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "4.3.0"
|
||||
}
|
||||
+71
@@ -0,0 +1,71 @@
|
||||
function Message() {
|
||||
Object.defineProperties(
|
||||
this, {
|
||||
data: {
|
||||
enumerable: true,
|
||||
get: getData,
|
||||
set: setData
|
||||
},
|
||||
type: {
|
||||
enumerable: true,
|
||||
get: getType,
|
||||
set: setType
|
||||
},
|
||||
load:{
|
||||
enumarable:true,
|
||||
writable:false,
|
||||
value:parse
|
||||
},
|
||||
JSON: {
|
||||
enumerable: true,
|
||||
get: getJSON
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var type = '';
|
||||
var data = {};
|
||||
|
||||
function getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
function getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
function getJSON() {
|
||||
return JSON.stringify(
|
||||
{
|
||||
type: type,
|
||||
data: data
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function setType(value) {
|
||||
type = value;
|
||||
}
|
||||
|
||||
function setData(value) {
|
||||
data = value;
|
||||
}
|
||||
|
||||
function parse(message){
|
||||
try{
|
||||
var message=JSON.parse(message);
|
||||
type=message.type;
|
||||
data=message.data;
|
||||
}catch(err){
|
||||
var badMessage=message;
|
||||
type='error',
|
||||
data={
|
||||
message:'Invalid JSON response format',
|
||||
err:err,
|
||||
response:badMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports=Message;
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
(
|
||||
function(){
|
||||
function Message() {
|
||||
Object.defineProperties(
|
||||
this, {
|
||||
data: {
|
||||
enumerable: true,
|
||||
get: getData,
|
||||
set: setData
|
||||
},
|
||||
type: {
|
||||
enumerable: true,
|
||||
get: getType,
|
||||
set: setType
|
||||
},
|
||||
load:{
|
||||
enumarable:true,
|
||||
writable:false,
|
||||
value:parse
|
||||
},
|
||||
JSON: {
|
||||
enumerable: true,
|
||||
get: getJSON
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var type = '';
|
||||
var data = {};
|
||||
|
||||
function getType() {
|
||||
return type;
|
||||
}
|
||||
|
||||
function getData() {
|
||||
return data;
|
||||
}
|
||||
|
||||
function getJSON() {
|
||||
return JSON.stringify(
|
||||
{
|
||||
type: type,
|
||||
data: data
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function setType(value) {
|
||||
type = value;
|
||||
}
|
||||
|
||||
function setData(value) {
|
||||
data = value;
|
||||
}
|
||||
|
||||
function parse(message){
|
||||
try{
|
||||
var message=JSON.parse(message);
|
||||
type=message.type;
|
||||
data=message.data;
|
||||
}catch(err){
|
||||
var badMessage=message;
|
||||
type='error',
|
||||
data={
|
||||
message:'Invalid JSON response format',
|
||||
err:err,
|
||||
response:badMessage
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
window.message=Message;
|
||||
}
|
||||
)();
|
||||
+81
@@ -0,0 +1,81 @@
|
||||
{
|
||||
"_from": "js-message@1.0.5",
|
||||
"_id": "js-message@1.0.5",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-IwDSSxrwjondCVvBpMnJz8uJLRU=",
|
||||
"_location": "/js-message",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "js-message@1.0.5",
|
||||
"name": "js-message",
|
||||
"escapedName": "js-message",
|
||||
"rawSpec": "1.0.5",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "1.0.5"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/node-ipc"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/js-message/-/js-message-1.0.5.tgz",
|
||||
"_shasum": "2300d24b1af08e89dd095bc1a4c9c9cfcb892d15",
|
||||
"_spec": "js-message@1.0.5",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server\\node_modules\\node-ipc",
|
||||
"author": {
|
||||
"name": "Brandon Nozaki Miller"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/RIAEvangelist/js-message/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {},
|
||||
"deprecated": false,
|
||||
"description": "normalized JS Object and JSON message and event protocol for node.js, vanialla js, react.js, components, actions, stores and dispatchers",
|
||||
"devDependencies": {},
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
},
|
||||
"homepage": "https://github.com/RIAEvangelist/js-message#readme",
|
||||
"keywords": [
|
||||
"message",
|
||||
"normalize",
|
||||
"events",
|
||||
"js",
|
||||
"json",
|
||||
"protocol",
|
||||
"ipc",
|
||||
"node",
|
||||
"nodejs",
|
||||
"node.js",
|
||||
"react",
|
||||
"react.js",
|
||||
"reactjs",
|
||||
"websocket",
|
||||
"websockets",
|
||||
"web",
|
||||
"socket",
|
||||
"sockets",
|
||||
"ws",
|
||||
"flux",
|
||||
"reflux",
|
||||
"component",
|
||||
"components",
|
||||
"store",
|
||||
"stores",
|
||||
"action",
|
||||
"actions"
|
||||
],
|
||||
"license": "DBAD",
|
||||
"main": "Message.js",
|
||||
"name": "js-message",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/RIAEvangelist/js-message.git"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node devServer.js",
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "1.0.5"
|
||||
}
|
||||
+62
@@ -0,0 +1,62 @@
|
||||
{
|
||||
"_from": "js-queue@2.0.0",
|
||||
"_id": "js-queue@2.0.0",
|
||||
"_inBundle": false,
|
||||
"_integrity": "sha1-NiITz4YPRo8BJfxslqvBdCUx+Ug=",
|
||||
"_location": "/js-queue",
|
||||
"_phantomChildren": {},
|
||||
"_requested": {
|
||||
"type": "version",
|
||||
"registry": true,
|
||||
"raw": "js-queue@2.0.0",
|
||||
"name": "js-queue",
|
||||
"escapedName": "js-queue",
|
||||
"rawSpec": "2.0.0",
|
||||
"saveSpec": null,
|
||||
"fetchSpec": "2.0.0"
|
||||
},
|
||||
"_requiredBy": [
|
||||
"/node-ipc"
|
||||
],
|
||||
"_resolved": "https://registry.npmjs.org/js-queue/-/js-queue-2.0.0.tgz",
|
||||
"_shasum": "362213cf860f468f0125fc6c96abc1742531f948",
|
||||
"_spec": "js-queue@2.0.0",
|
||||
"_where": "C:\\Users\\Luis\\source\\repos\\AAMasters\\Events-Server\\node_modules\\node-ipc",
|
||||
"author": {
|
||||
"name": "Brandon Nozaki Miller"
|
||||
},
|
||||
"bugs": {
|
||||
"url": "https://github.com/RIAEvangelist/js-queue/issues"
|
||||
},
|
||||
"bundleDependencies": false,
|
||||
"dependencies": {
|
||||
"easy-stack": "^1.0.0"
|
||||
},
|
||||
"deprecated": false,
|
||||
"description": "Simple JS queue with auto run for node and browsers",
|
||||
"engines": {
|
||||
"node": ">=1.0.0"
|
||||
},
|
||||
"homepage": "https://github.com/RIAEvangelist/js-queue#readme",
|
||||
"keywords": [
|
||||
"queue",
|
||||
"node",
|
||||
"js",
|
||||
"auto",
|
||||
"run",
|
||||
"execute",
|
||||
"browser",
|
||||
"react"
|
||||
],
|
||||
"license": "DBAD",
|
||||
"main": "queue.js",
|
||||
"name": "js-queue",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/RIAEvangelist/js-queue.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"version": "2.0.0"
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
function Queue(){
|
||||
Object.defineProperties(
|
||||
this,
|
||||
{
|
||||
add:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:addToQueue
|
||||
},
|
||||
next:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:run
|
||||
},
|
||||
clear:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:clearQueue
|
||||
},
|
||||
contents:{
|
||||
enumerable:false,
|
||||
get:getQueue,
|
||||
set:setQueue
|
||||
},
|
||||
autoRun:{
|
||||
enumerable:true,
|
||||
writable:true,
|
||||
value:true
|
||||
},
|
||||
stop:{
|
||||
enumerable:true,
|
||||
writable:true,
|
||||
value:false
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var queue=[];
|
||||
var running=false;
|
||||
var stop=false;
|
||||
|
||||
function clearQueue(){
|
||||
queue=[];
|
||||
return queue;
|
||||
}
|
||||
|
||||
function getQueue(){
|
||||
return queue;
|
||||
}
|
||||
|
||||
function setQueue(val){
|
||||
queue=val;
|
||||
return queue;
|
||||
}
|
||||
|
||||
function addToQueue(){
|
||||
for(var i in arguments){
|
||||
queue.push(arguments[i]);
|
||||
}
|
||||
if(!running && !this.stop && this.autoRun){
|
||||
this.next();
|
||||
}
|
||||
}
|
||||
|
||||
function run(){
|
||||
running=true;
|
||||
if(queue.length<1 || this.stop){
|
||||
running=false;
|
||||
return;
|
||||
}
|
||||
|
||||
queue.shift().bind(this)();
|
||||
}
|
||||
}
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
function Queue(asStack){
|
||||
Object.defineProperties(
|
||||
this,
|
||||
{
|
||||
add:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:addToQueue
|
||||
},
|
||||
next:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:run
|
||||
},
|
||||
clear:{
|
||||
enumerable:true,
|
||||
writable:false,
|
||||
value:clearQueue
|
||||
},
|
||||
contents:{
|
||||
enumerable:false,
|
||||
get:getQueue,
|
||||
set:setQueue
|
||||
},
|
||||
autoRun:{
|
||||
enumerable:true,
|
||||
writable:true,
|
||||
value:true
|
||||
},
|
||||
stop:{
|
||||
enumerable:true,
|
||||
writable:true,
|
||||
value:false
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
var queue=[];
|
||||
var running=false;
|
||||
var stop=false;
|
||||
|
||||
function clearQueue(){
|
||||
queue=[];
|
||||
return queue;
|
||||
}
|
||||
|
||||
function getQueue(){
|
||||
return queue;
|
||||
}
|
||||
|
||||
function setQueue(val){
|
||||
queue=val;
|
||||
return queue;
|
||||
}
|
||||
|
||||
function addToQueue(){
|
||||
for(var i in arguments){
|
||||
queue.push(arguments[i]);
|
||||
}
|
||||
if(!running && !this.stop && this.autoRun){
|
||||
this.next();
|
||||
}
|
||||
}
|
||||
|
||||
function run(){
|
||||
running=true;
|
||||
if(queue.length<1 || this.stop){
|
||||
running=false;
|
||||
return;
|
||||
}
|
||||
|
||||
queue.shift().bind(this)();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports=Queue;
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
const Stack=require('easy-stack');
|
||||
|
||||
module.exports = Stack;
|
||||
+257
@@ -0,0 +1,257 @@
|
||||
'use strict';
|
||||
|
||||
const net = require('net'),
|
||||
tls = require('tls'),
|
||||
EventParser = require('../entities/EventParser.js'),
|
||||
Message = require('js-message'),
|
||||
fs = require('fs'),
|
||||
Queue = require('js-queue');
|
||||
|
||||
let Events = require('event-pubsub/es5');
|
||||
if(process.version[1]>4){
|
||||
Events = require('event-pubsub');
|
||||
}
|
||||
|
||||
let eventParser = new EventParser();
|
||||
|
||||
class Client extends Events{
|
||||
constructor(config,log){
|
||||
super();
|
||||
Object.assign(
|
||||
this,
|
||||
{
|
||||
Client : Client,
|
||||
config : config,
|
||||
queue : new Queue,
|
||||
socket : false,
|
||||
connect : connect,
|
||||
emit : emit,
|
||||
log : log,
|
||||
retriesRemaining:config.maxRetries||0,
|
||||
explicitlyDisconnected: false
|
||||
}
|
||||
);
|
||||
|
||||
eventParser=new EventParser(this.config);
|
||||
}
|
||||
}
|
||||
|
||||
function emit(type,data){
|
||||
this.log('dispatching event to ', this.id, this.path, ' : ', type, ',', data);
|
||||
|
||||
let message=new Message;
|
||||
message.type=type;
|
||||
message.data=data;
|
||||
|
||||
if(this.config.rawBuffer){
|
||||
message=new Buffer(type,this.config.encoding);
|
||||
}else{
|
||||
message=eventParser.format(message);
|
||||
}
|
||||
|
||||
if(!this.config.sync){
|
||||
this.socket.write(message);
|
||||
return;
|
||||
}
|
||||
|
||||
this.queue.add(
|
||||
syncEmit.bind(this,message)
|
||||
);
|
||||
}
|
||||
|
||||
function syncEmit(message){
|
||||
this.log('dispatching event to ', this.id, this.path, ' : ', message);
|
||||
this.socket.write(message);
|
||||
}
|
||||
|
||||
function connect(){
|
||||
//init client object for scope persistance especially inside of socket events.
|
||||
let client=this;
|
||||
|
||||
client.log('requested connection to ', client.id, client.path);
|
||||
if(!this.path){
|
||||
client.log('\n\n######\nerror: ', client.id ,' client has not specified socket path it wishes to connect to.');
|
||||
return;
|
||||
}
|
||||
|
||||
const options={};
|
||||
|
||||
if(!client.port){
|
||||
client.log('Connecting client on Unix Socket :', client.path);
|
||||
|
||||
options.path=client.path;
|
||||
|
||||
if (process.platform ==='win32' && !client.path.startsWith('\\\\.\\pipe\\')){
|
||||
options.path = options.path.replace(/^\//, '');
|
||||
options.path = options.path.replace(/\//g, '-');
|
||||
options.path= `\\\\.\\pipe\\${options.path}`;
|
||||
}
|
||||
|
||||
client.socket = net.connect(options);
|
||||
}else{
|
||||
options.host=client.path;
|
||||
options.port=client.port;
|
||||
|
||||
if(client.config.interface.localAddress){
|
||||
options.localAddress=client.config.interface.localAddress;
|
||||
}
|
||||
|
||||
if(client.config.interface.localPort){
|
||||
options.localPort=client.config.interface.localPort;
|
||||
}
|
||||
|
||||
if(client.config.interface.family){
|
||||
options.family=client.config.interface.family;
|
||||
}
|
||||
|
||||
if(client.config.interface.hints){
|
||||
options.hints=client.config.interface.hints;
|
||||
}
|
||||
|
||||
if(client.config.interface.lookup){
|
||||
options.lookup=client.config.interface.lookup;
|
||||
}
|
||||
|
||||
if(!client.config.tls){
|
||||
client.log('Connecting client via TCP to', options);
|
||||
client.socket = net.connect(options);
|
||||
}else{
|
||||
client.log('Connecting client via TLS to', client.path ,client.port,client.config.tls);
|
||||
if(client.config.tls.private){
|
||||
client.config.tls.key=fs.readFileSync(client.config.tls.private);
|
||||
}
|
||||
if(client.config.tls.public){
|
||||
client.config.tls.cert=fs.readFileSync(client.config.tls.public);
|
||||
}
|
||||
if(client.config.tls.trustedConnections){
|
||||
if(typeof client.config.tls.trustedConnections === 'string'){
|
||||
client.config.tls.trustedConnections=[client.config.tls.trustedConnections];
|
||||
}
|
||||
client.config.tls.ca=[];
|
||||
for(let i=0; i<client.config.tls.trustedConnections.length; i++){
|
||||
client.config.tls.ca.push(
|
||||
fs.readFileSync(client.config.tls.trustedConnections[i])
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Object.assign(client.config.tls,options);
|
||||
|
||||
client.socket = tls.connect(
|
||||
client.config.tls
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
client.socket.setEncoding(this.config.encoding);
|
||||
|
||||
client.socket.on(
|
||||
'error',
|
||||
function(err){
|
||||
client.log('\n\n######\nerror: ', err);
|
||||
client.publish('error', err);
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
client.socket.on(
|
||||
'connect',
|
||||
function connectionMade(){
|
||||
client.publish('connect');
|
||||
client.retriesRemaining=client.config.maxRetries;
|
||||
client.log('retrying reset');
|
||||
}
|
||||
);
|
||||
|
||||
client.socket.on(
|
||||
'close',
|
||||
function connectionClosed(){
|
||||
client.log('connection closed' ,client.id , client.path,
|
||||
client.retriesRemaining, 'tries remaining of', client.config.maxRetries
|
||||
);
|
||||
|
||||
if(
|
||||
client.config.stopRetrying ||
|
||||
client.retriesRemaining<1 ||
|
||||
client.explicitlyDisconnected
|
||||
|
||||
){
|
||||
client.publish('disconnect');
|
||||
client.log(
|
||||
(client.config.id),
|
||||
'exceeded connection rety amount of',
|
||||
' or stopRetrying flag set.'
|
||||
);
|
||||
|
||||
client.socket.destroy();
|
||||
client.publish('destroy');
|
||||
client=undefined;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(
|
||||
function retryTimeout(){
|
||||
client.retriesRemaining--;
|
||||
client.connect();
|
||||
}.bind(null,client),
|
||||
client.config.retry
|
||||
);
|
||||
|
||||
client.publish('disconnect');
|
||||
}
|
||||
);
|
||||
|
||||
client.socket.on(
|
||||
'data',
|
||||
function(data) {
|
||||
client.log('## received events ##');
|
||||
if(client.config.rawBuffer){
|
||||
client.publish(
|
||||
'data',
|
||||
new Buffer(data,client.config.encoding)
|
||||
);
|
||||
if(!client.config.sync){
|
||||
return;
|
||||
}
|
||||
|
||||
client.queue.next();
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.ipcBuffer){
|
||||
this.ipcBuffer='';
|
||||
}
|
||||
|
||||
data=(this.ipcBuffer+=data);
|
||||
|
||||
if(data.slice(-1)!=eventParser.delimiter || data.indexOf(eventParser.delimiter) == -1){
|
||||
client.log('Messages are large, You may want to consider smaller messages.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.ipcBuffer='';
|
||||
|
||||
const events = eventParser.parse(data);
|
||||
const eCount = events.length;
|
||||
for(let i=0; i<eCount; i++){
|
||||
let message=new Message;
|
||||
message.load(events[i]);
|
||||
|
||||
client.log('detected event', message.type, message.data);
|
||||
client.publish(
|
||||
message.type,
|
||||
message.data
|
||||
);
|
||||
}
|
||||
|
||||
if(!client.config.sync){
|
||||
return;
|
||||
}
|
||||
|
||||
client.queue.next();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports=Client;
|
||||
+402
@@ -0,0 +1,402 @@
|
||||
'use strict';
|
||||
|
||||
const net = require('net'),
|
||||
tls = require('tls'),
|
||||
fs = require('fs'),
|
||||
dgram = require('dgram'),
|
||||
EventParser = require('../entities/EventParser.js'),
|
||||
Message = require('js-message');
|
||||
|
||||
let Events = require('event-pubsub/es5');
|
||||
if(process.version[1]>4){
|
||||
Events = require('event-pubsub');
|
||||
}
|
||||
|
||||
let eventParser = new EventParser();
|
||||
|
||||
class Server extends Events{
|
||||
constructor(path,config,log,port){
|
||||
super();
|
||||
Object.assign(
|
||||
this,
|
||||
{
|
||||
config : config,
|
||||
path : path,
|
||||
port : port,
|
||||
udp4 : false,
|
||||
udp6 : false,
|
||||
log : log,
|
||||
server : false,
|
||||
sockets : [],
|
||||
emit : emit,
|
||||
broadcast : broadcast
|
||||
}
|
||||
);
|
||||
|
||||
eventParser=new EventParser(this.config);
|
||||
|
||||
this.on(
|
||||
'close',
|
||||
serverClosed.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
onStart(socket){
|
||||
this.trigger(
|
||||
'start',
|
||||
socket
|
||||
);
|
||||
}
|
||||
|
||||
stop(){
|
||||
this.server.close();
|
||||
}
|
||||
|
||||
start(){
|
||||
if(!this.path){
|
||||
this.log('Socket Server Path not specified, refusing to start');
|
||||
return;
|
||||
}
|
||||
|
||||
if(this.config.unlink){
|
||||
fs.unlink(
|
||||
this.path,
|
||||
startServer.bind(this)
|
||||
);
|
||||
}else{
|
||||
startServer.bind(this)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function emit(socket, type, data){
|
||||
this.log('dispatching event to socket', ' : ', type, data);
|
||||
|
||||
let message=new Message;
|
||||
message.type=type;
|
||||
message.data=data;
|
||||
|
||||
if(this.config.rawBuffer){
|
||||
this.log(this.config.encoding)
|
||||
message=new Buffer(type,this.config.encoding);
|
||||
}else{
|
||||
message=eventParser.format(message);
|
||||
}
|
||||
|
||||
if(this.udp4 || this.udp6){
|
||||
|
||||
if(!socket.address || !socket.port){
|
||||
this.log('Attempting to emit to a single UDP socket without supplying socket address or port. Redispatching event as broadcast to all connected sockets');
|
||||
this.broadcast(type,data);
|
||||
return;
|
||||
}
|
||||
|
||||
this.server.write(
|
||||
message,
|
||||
socket
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
socket.write(message);
|
||||
}
|
||||
|
||||
function broadcast(type,data){
|
||||
this.log('broadcasting event to all known sockets listening to ', this.path,' : ', ((this.port)?this.port:''), type, data);
|
||||
let message=new Message;
|
||||
message.type=type;
|
||||
message.data=data;
|
||||
|
||||
if(this.config.rawBuffer){
|
||||
message=new Buffer(type,this.config.encoding);
|
||||
}else{
|
||||
message=eventParser.format(message);
|
||||
}
|
||||
|
||||
if(this.udp4 || this.udp6){
|
||||
for(let i=1, count=this.sockets.length; i<count; i++){
|
||||
this.server.write(message,this.sockets[i]);
|
||||
}
|
||||
}else{
|
||||
for(let i=0, count=this.sockets.length; i<count; i++){
|
||||
this.sockets[i].write(message);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function serverClosed(){
|
||||
for(let i=0, count=this.sockets.length; i<count; i++){
|
||||
let socket=this.sockets[i];
|
||||
let destroyedSocketId=false;
|
||||
|
||||
if(socket){
|
||||
if(socket.readable){
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if(socket.id){
|
||||
destroyedSocketId=socket.id;
|
||||
}
|
||||
|
||||
this.log('socket disconnected',destroyedSocketId.toString());
|
||||
|
||||
if(socket && socket.destroy){
|
||||
socket.destroy();
|
||||
}
|
||||
|
||||
this.sockets.splice(i,1);
|
||||
|
||||
this.publish('socket.disconnected', socket, destroyedSocketId);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function gotData(socket,data,UDPSocket){
|
||||
let sock=((this.udp4 || this.udp6)? UDPSocket : socket);
|
||||
if(this.config.rawBuffer){
|
||||
data=new Buffer(data,this.config.encoding);
|
||||
this.publish(
|
||||
'data',
|
||||
data,
|
||||
sock
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.ipcBuffer){
|
||||
this.ipcBuffer='';
|
||||
}
|
||||
|
||||
data=(this.ipcBuffer+=data);
|
||||
|
||||
if(data.slice(-1)!=eventParser.delimiter || data.indexOf(eventParser.delimiter) == -1){
|
||||
this.log('Messages are large, You may want to consider smaller messages.');
|
||||
return;
|
||||
}
|
||||
|
||||
this.ipcBuffer='';
|
||||
|
||||
data=eventParser.parse(data);
|
||||
|
||||
while(data.length>0){
|
||||
let message=new Message;
|
||||
message.load(data.shift());
|
||||
|
||||
// Only set the sock id if it is specified.
|
||||
if (message.data && message.data.id){
|
||||
sock.id=message.data.id;
|
||||
}
|
||||
|
||||
this.log('received event of : ',message.type,message.data);
|
||||
|
||||
this.publish(
|
||||
message.type,
|
||||
message.data,
|
||||
sock
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
function socketClosed(socket){
|
||||
this.publish(
|
||||
'close',
|
||||
socket
|
||||
);
|
||||
}
|
||||
|
||||
function serverCreated(socket) {
|
||||
this.sockets.push(socket);
|
||||
|
||||
if(socket.setEncoding){
|
||||
socket.setEncoding(this.config.encoding);
|
||||
}
|
||||
|
||||
this.log('## socket connection to server detected ##');
|
||||
socket.on(
|
||||
'close',
|
||||
socketClosed.bind(this)
|
||||
);
|
||||
|
||||
socket.on(
|
||||
'error',
|
||||
function(err){
|
||||
this.log('server socket error',err);
|
||||
|
||||
this.publish('error',err);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
socket.on(
|
||||
'data',
|
||||
gotData.bind(this,socket)
|
||||
);
|
||||
|
||||
socket.on(
|
||||
'message',
|
||||
function(msg,rinfo) {
|
||||
if (!rinfo){
|
||||
return;
|
||||
}
|
||||
|
||||
this.log('Received UDP message from ', rinfo.address, rinfo.port);
|
||||
let data;
|
||||
|
||||
if(this.config.rawSocket){
|
||||
data=new Buffer(msg,this.config.encoding);
|
||||
}else{
|
||||
data=msg.toString();
|
||||
}
|
||||
socket.emit('data',data,rinfo);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.publish(
|
||||
'connect',
|
||||
socket
|
||||
);
|
||||
|
||||
if(this.config.rawBuffer){
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
function startServer() {
|
||||
this.log(
|
||||
'starting server on ',this.path,
|
||||
((this.port)?`:${this.port}`:'')
|
||||
);
|
||||
|
||||
if(!this.udp4 && !this.udp6){
|
||||
this.log('starting TLS server',this.config.tls);
|
||||
if(!this.config.tls){
|
||||
this.server=net.createServer(
|
||||
serverCreated.bind(this)
|
||||
);
|
||||
}else{
|
||||
startTLSServer.bind(this)();
|
||||
}
|
||||
}else{
|
||||
this.server=dgram.createSocket(
|
||||
((this.udp4)? 'udp4':'udp6')
|
||||
);
|
||||
this.server.write=UDPWrite.bind(this);
|
||||
this.server.on(
|
||||
'listening',
|
||||
function UDPServerStarted() {
|
||||
serverCreated.bind(this)(this.server);
|
||||
}.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
this.server.on(
|
||||
'error',
|
||||
function(err){
|
||||
this.log('server error',err);
|
||||
|
||||
this.publish(
|
||||
'error',
|
||||
err
|
||||
);
|
||||
}.bind(this)
|
||||
);
|
||||
|
||||
this.server.maxConnections=this.config.maxConnections;
|
||||
|
||||
if(!this.port){
|
||||
this.log('starting server as', 'Unix || Windows Socket');
|
||||
if (process.platform ==='win32'){
|
||||
this.path = this.path.replace(/^\//, '');
|
||||
this.path = this.path.replace(/\//g, '-');
|
||||
this.path= `\\\\.\\pipe\\${this.path}`;
|
||||
}
|
||||
|
||||
this.server.listen(
|
||||
this.path,
|
||||
this.onStart.bind(this)
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!this.udp4 && !this.udp6){
|
||||
this.log('starting server as', (this.config.tls?'TLS':'TCP'));
|
||||
this.server.listen(
|
||||
this.port,
|
||||
this.path,
|
||||
this.onStart.bind(this)
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
this.log('starting server as',((this.udp4)? 'udp4':'udp6'));
|
||||
|
||||
this.server.bind(
|
||||
this.port,
|
||||
this.path
|
||||
);
|
||||
|
||||
this.onStart(
|
||||
{
|
||||
address : this.path,
|
||||
port : this.port
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function startTLSServer(){
|
||||
this.log('starting TLS server',this.config.tls);
|
||||
if(this.config.tls.private){
|
||||
this.config.tls.key=fs.readFileSync(this.config.tls.private);
|
||||
}else{
|
||||
this.config.tls.key=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/private/server.key`);
|
||||
}
|
||||
if(this.config.tls.public){
|
||||
this.config.tls.cert=fs.readFileSync(this.config.tls.public);
|
||||
}else{
|
||||
this.config.tls.cert=fs.readFileSync(`${__dirname}/../local-node-ipc-certs/server.pub`);
|
||||
}
|
||||
if(this.config.tls.dhparam){
|
||||
this.config.tls.dhparam=fs.readFileSync(this.config.tls.dhparam);
|
||||
}
|
||||
if(this.config.tls.trustedConnections){
|
||||
if(typeof this.config.tls.trustedConnections === 'string'){
|
||||
this.config.tls.trustedConnections=[this.config.tls.trustedConnections];
|
||||
}
|
||||
this.config.tls.ca=[];
|
||||
for(let i=0; i<this.config.tls.trustedConnections.length; i++){
|
||||
this.config.tls.ca.push(
|
||||
fs.readFileSync(this.config.tls.trustedConnections[i])
|
||||
);
|
||||
}
|
||||
}
|
||||
this.server=tls.createServer(
|
||||
this.config.tls,
|
||||
serverCreated.bind(this)
|
||||
);
|
||||
}
|
||||
|
||||
function UDPWrite(message,socket){
|
||||
let data=new Buffer(message, this.config.encoding);
|
||||
this.server.send(
|
||||
data,
|
||||
0,
|
||||
data.length,
|
||||
socket.port,
|
||||
socket.address,
|
||||
function(err, bytes) {
|
||||
if(err){
|
||||
this.log('error writing data to socket',err);
|
||||
this.publish(
|
||||
'error',
|
||||
function(err){
|
||||
this.publish('error',err);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
module.exports=Server;
|
||||
+80
@@ -0,0 +1,80 @@
|
||||
'use strict';
|
||||
|
||||
/*eslint no-magic-numbers: ["error", { "ignore": [ 0] }]*/
|
||||
|
||||
/**
|
||||
* @module entities
|
||||
*/
|
||||
|
||||
const os = require('os');
|
||||
|
||||
/**
|
||||
* @class Defaults
|
||||
* @description Defaults Entity
|
||||
*/
|
||||
class Defaults{
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
* @method constructor
|
||||
* @return {void}
|
||||
*/
|
||||
constructor(){
|
||||
|
||||
this.appspace='app.';
|
||||
this.socketRoot='/tmp/';
|
||||
this.id=os.hostname();
|
||||
|
||||
this.encoding='utf8';
|
||||
this.rawBuffer=false;
|
||||
this.sync=false;
|
||||
this.unlink=true;
|
||||
|
||||
this.delimiter='\f';
|
||||
|
||||
this.silent=false;
|
||||
this.logDepth=5;
|
||||
this.logInColor=true;
|
||||
this.logger=console.log.bind(console);
|
||||
|
||||
this.maxConnections=100;
|
||||
this.retry=500;
|
||||
this.maxRetries=Infinity;
|
||||
this.stopRetrying=false;
|
||||
|
||||
this.IPType=getIPType();
|
||||
this.tls=false;
|
||||
this.networkHost = (this.IPType == 'IPv6') ? '::1' : '127.0.0.1';
|
||||
this.networkPort = 8000;
|
||||
|
||||
this.interface={
|
||||
localAddress:false,
|
||||
localPort:false,
|
||||
family:false,
|
||||
hints:false,
|
||||
lookup:false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* method to get ip type
|
||||
*
|
||||
* @method getIPType
|
||||
* @return {string} ip type
|
||||
*/
|
||||
function getIPType() {
|
||||
const networkInterfaces = os.networkInterfaces();
|
||||
let IPType = '';
|
||||
if (networkInterfaces
|
||||
&& Array.isArray(networkInterfaces)
|
||||
&& networkInterfaces.length > 0) {
|
||||
// getting the family of first network interface available
|
||||
IPType = networkInterfaces [
|
||||
Object.keys( networkInterfaces )[0]
|
||||
][0].family;
|
||||
}
|
||||
return IPType;
|
||||
}
|
||||
|
||||
module.exports=Defaults;
|
||||
+32
@@ -0,0 +1,32 @@
|
||||
'use strict';
|
||||
|
||||
const Defaults = require('./Defaults.js');
|
||||
|
||||
class Parser{
|
||||
constructor(config){
|
||||
if(!config){
|
||||
config=new Defaults;
|
||||
}
|
||||
this.delimiter=config.delimiter;
|
||||
}
|
||||
|
||||
format(message){
|
||||
if(!message.data && message.data!==false && message.data!==0){
|
||||
message.data={};
|
||||
}
|
||||
if(message.data['_maxListeners']){
|
||||
message.data={};
|
||||
}
|
||||
|
||||
message=message.JSON+this.delimiter;
|
||||
return message;
|
||||
}
|
||||
|
||||
parse(data){
|
||||
let events=data.split(this.delimiter);
|
||||
events.pop();
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports=Parser;
|
||||
Generated
Vendored
+44
@@ -0,0 +1,44 @@
|
||||
const ipc=require('../../../node-ipc');
|
||||
|
||||
/***************************************\
|
||||
*
|
||||
* You should start both hello and world
|
||||
* then you will see them communicating.
|
||||
*
|
||||
* *************************************/
|
||||
|
||||
ipc.config.id = 'goodbye';
|
||||
ipc.config.retry= 1500;
|
||||
ipc.config.maxRetries= 10;
|
||||
|
||||
ipc.connectToNet(
|
||||
'world',
|
||||
function(){
|
||||
ipc.of.world.on(
|
||||
'connect',
|
||||
function(){
|
||||
ipc.log('## connected to world ##', ipc.config.delay);
|
||||
ipc.of.world.emit(
|
||||
'app.message',
|
||||
{
|
||||
id : ipc.config.id,
|
||||
message : 'goodbye'
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'disconnect',
|
||||
function(){
|
||||
ipc.log('disconnected from world');
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'kill.connection',
|
||||
function(data){
|
||||
ipc.log('world requested kill.connection');
|
||||
ipc.disconnect('world');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
Generated
Vendored
+50
@@ -0,0 +1,50 @@
|
||||
const ipc=require('../../../node-ipc');
|
||||
|
||||
/***************************************\
|
||||
*
|
||||
* You should start both hello and world
|
||||
* then you will see them communicating.
|
||||
*
|
||||
* *************************************/
|
||||
|
||||
ipc.config.id = 'hello';
|
||||
ipc.config.retry= 1500;
|
||||
ipc.config.maxRetries=10;
|
||||
|
||||
ipc.connectToNet(
|
||||
'world',
|
||||
function(){
|
||||
ipc.of.world.on(
|
||||
'connect',
|
||||
function(){
|
||||
ipc.log('## connected to world ##', ipc.config.delay);
|
||||
ipc.of.world.emit(
|
||||
'app.message',
|
||||
{
|
||||
id : ipc.config.id,
|
||||
message : 'hello'
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'disconnect',
|
||||
function(){
|
||||
ipc.log('disconnected from world');
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'app.message',
|
||||
function(data){
|
||||
ipc.log('got a message from world : ', data.message);
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'kill.connection',
|
||||
function(data){
|
||||
ipc.log('world requested kill.connection');
|
||||
ipc.disconnect('world');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
Generated
Vendored
+51
@@ -0,0 +1,51 @@
|
||||
const ipc=require('../../../node-ipc');
|
||||
|
||||
/***************************************\
|
||||
*
|
||||
* You should start both hello and world
|
||||
* then you will see them communicating.
|
||||
*
|
||||
* *************************************/
|
||||
|
||||
ipc.config.id = 'world';
|
||||
ipc.config.retry= 1500;
|
||||
|
||||
var messages={
|
||||
goodbye:false,
|
||||
hello:false
|
||||
};
|
||||
|
||||
ipc.serveNet(
|
||||
function(){
|
||||
ipc.server.on(
|
||||
'app.message',
|
||||
function(data,socket){
|
||||
ipc.log('got a message from', (data.id), (data.message));
|
||||
messages[data.id]=true;
|
||||
ipc.server.emit(
|
||||
socket,
|
||||
'app.message',
|
||||
{
|
||||
id : ipc.config.id,
|
||||
message : data.message+' world!'
|
||||
}
|
||||
);
|
||||
|
||||
if(messages.hello && messages.goodbye){
|
||||
ipc.log('got all required events, telling clients to kill connection');
|
||||
ipc.server.broadcast(
|
||||
'kill.connection',
|
||||
{
|
||||
id:ipc.config.id
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
|
||||
ipc.server.start();
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
const ipc=require('../../../node-ipc');
|
||||
|
||||
/***************************************\
|
||||
*
|
||||
* You should start both hello and world
|
||||
* then you will see them communicating.
|
||||
*
|
||||
* *************************************/
|
||||
|
||||
ipc.config.id = 'hello';
|
||||
ipc.config.retry= 1500;
|
||||
|
||||
ipc.connectToNet(
|
||||
'world',
|
||||
function(){
|
||||
ipc.of.world.on(
|
||||
'connect',
|
||||
function(){
|
||||
ipc.log('## connected to world ##', ipc.config.delay);
|
||||
ipc.of.world.emit(
|
||||
'message',
|
||||
'hello'
|
||||
);
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'disconnect',
|
||||
function(){
|
||||
ipc.log('disconnected from world');
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'message',
|
||||
function(data){
|
||||
ipc.log('got a message from world : ', data);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
const ipc=require('../../../node-ipc');
|
||||
|
||||
/***************************************\
|
||||
*
|
||||
* You should start both hello and world
|
||||
* then you will see them communicating.
|
||||
*
|
||||
* *************************************/
|
||||
|
||||
ipc.config.id = 'world';
|
||||
ipc.config.retry= 1500;
|
||||
ipc.config.maxConnections=1;
|
||||
|
||||
ipc.serveNet(
|
||||
function(){
|
||||
ipc.server.on(
|
||||
'message',
|
||||
function(data,socket){
|
||||
ipc.log('got a message : ', data);
|
||||
ipc.server.emit(
|
||||
socket,
|
||||
'message',
|
||||
data+' world!'
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
ipc.server.on(
|
||||
'socket.disconnected',
|
||||
function(data,socket){
|
||||
console.log('DISCONNECTED\n\n',arguments);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
ipc.server.on(
|
||||
'error',
|
||||
function(err){
|
||||
ipc.log('Got an ERROR!',err);
|
||||
}
|
||||
);
|
||||
|
||||
ipc.server.start();
|
||||
+46
@@ -0,0 +1,46 @@
|
||||
const ipc=require('../../../node-ipc');
|
||||
|
||||
/***************************************\
|
||||
*
|
||||
* You should start both hello and world
|
||||
* then you will see them communicating.
|
||||
*
|
||||
* *************************************/
|
||||
|
||||
ipc.config.id = 'hello';
|
||||
ipc.config.retry= 1500;
|
||||
ipc.config.sync= true;
|
||||
|
||||
ipc.connectToNet(
|
||||
'world',
|
||||
function(){
|
||||
ipc.of.world.on(
|
||||
'connect',
|
||||
function(){
|
||||
ipc.log('## connected to world ##', ipc.config.delay);
|
||||
|
||||
//queue up a bunch of requests to be sent synchronously
|
||||
for(var i=0; i<10; i++){
|
||||
ipc.of.world.emit(
|
||||
'message',
|
||||
'hello'+i
|
||||
);
|
||||
}
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'disconnect',
|
||||
function(){
|
||||
ipc.log('disconnected from world');
|
||||
}
|
||||
);
|
||||
ipc.of.world.on(
|
||||
'message',
|
||||
function(data){
|
||||
ipc.log('got a message from world : ', data,'\n\n');
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
console.log(ipc);
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
const ipc=require('../../../node-ipc');
|
||||
|
||||
/***************************************\
|
||||
*
|
||||
* You should start both hello and world
|
||||
* then you will see them communicating.
|
||||
*
|
||||
* *************************************/
|
||||
|
||||
ipc.config.id = 'world';
|
||||
ipc.config.retry= 1500;
|
||||
ipc.config.sync = true;
|
||||
|
||||
ipc.serveNet(
|
||||
function(){
|
||||
ipc.server.on(
|
||||
'message',
|
||||
function(data,socket){
|
||||
ipc.log('got a message : ', data);
|
||||
//fake some synch procedural code
|
||||
setTimeout(
|
||||
function(){
|
||||
ipc.server.emit(
|
||||
socket,
|
||||
'message',
|
||||
data+' world!'
|
||||
);
|
||||
},
|
||||
3000
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
ipc.server.on(
|
||||
'socket.disconnected',
|
||||
function(data,socket){
|
||||
console.log(arguments);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
|
||||
ipc.server.start();
|
||||
+35
@@ -0,0 +1,35 @@
|
||||
const ipc=require('../../../node-ipc');
|
||||
|
||||
/***************************************\
|
||||
*
|
||||
* You should start both hello and world
|
||||
* then you will see them communicating.
|
||||
*
|
||||
* *************************************/
|
||||
|
||||
ipc.config.id = 'hello';
|
||||
ipc.config.retry= 1500;
|
||||
ipc.config.rawBuffer=true;
|
||||
ipc.config.encoding='ascii';
|
||||
|
||||
ipc.connectToNet(
|
||||
'world',
|
||||
function(){
|
||||
ipc.of.world.on(
|
||||
'connect',
|
||||
function(){
|
||||
ipc.log('## connected to world ##', ipc.config.delay);
|
||||
ipc.of.world.emit(
|
||||
'hello'
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
ipc.of.world.on(
|
||||
'data',
|
||||
function(data){
|
||||
ipc.log('got a message from world : ', data,data.toString());
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
Loaded 100 of 3448 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user