447 lines
15 KiB
JavaScript
447 lines
15 KiB
JavaScript
/*
|
|
|
|
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: false,
|
|
container: undefined,
|
|
inViewport: undefined,
|
|
timeMachines: undefined,
|
|
viewport: undefined,
|
|
payload: undefined,
|
|
onKeyPressed: onKeyPressed,
|
|
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()
|
|
|
|
let isInitialized = false
|
|
|
|
return thisObject
|
|
|
|
function reset() {
|
|
finalize()
|
|
initialSetup()
|
|
isInitialized = false
|
|
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
|
|
|
|
if (thisObject.viewport !== undefined) {
|
|
thisObject.viewport.finalize()
|
|
thisObject.viewport = undefined
|
|
}
|
|
|
|
thisObject.payload = undefined
|
|
}
|
|
|
|
function initialize() {
|
|
if (isInitialized === true) { return }
|
|
if (canvas.designSpace === undefined) { return }
|
|
if (canvas.designSpace.workspace === undefined) { return }
|
|
|
|
let rootNodes = canvas.designSpace.workspace.workspaceNode.rootNodes
|
|
for (let i = 0; i < rootNodes.length; i++) {
|
|
let rootNode = rootNodes[i]
|
|
if (rootNode !== null) {
|
|
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)
|
|
|
|
isInitialized = true
|
|
}
|
|
|
|
function getContainer(point, purpose) {
|
|
if (thisObject.visible !== true) { return }
|
|
if (thisObject.viewport === undefined) { 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) {
|
|
if (containerFound === undefined) {
|
|
containerFound = container
|
|
}
|
|
} else {
|
|
if (thisObject.container.frame.isThisPointHere(point, true) === true) {
|
|
if (containerFound === undefined) {
|
|
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() {
|
|
if (thisObject.visible === false) { return }
|
|
let displaceVector = {
|
|
x: 0,
|
|
y: browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100
|
|
}
|
|
|
|
canvas.chartingSpace.viewport.displace(displaceVector)
|
|
}
|
|
|
|
function oneScreenDown() {
|
|
if (thisObject.visible === false) { return }
|
|
let displaceVector = {
|
|
x: 0,
|
|
y: -browserCanvas.height * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100
|
|
}
|
|
|
|
canvas.chartingSpace.viewport.displace(displaceVector)
|
|
}
|
|
|
|
function oneScreenLeft() {
|
|
if (thisObject.visible === false) { return }
|
|
let displaceVector = {
|
|
x: browserCanvas.width * PERCENTAGE_OF_SCREEN_FOR_DISPLACEMENT / 100,
|
|
y: 0
|
|
}
|
|
|
|
canvas.chartingSpace.viewport.displace(displaceVector, true)
|
|
}
|
|
|
|
function oneScreenRight() {
|
|
if (thisObject.visible === false) { return }
|
|
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() {
|
|
initialize()
|
|
thisObjectPhysics()
|
|
if (thisObject.visible !== true) { return }
|
|
if (thisObject.viewport !== undefined) {
|
|
thisObject.viewport.physics()
|
|
}
|
|
childrenPhysics()
|
|
syncWithDesigner()
|
|
}
|
|
|
|
function onKeyPressed(event) {
|
|
if (thisObject.visible !== true) { return }
|
|
for (let j = 0; j < thisObject.timeMachines.length; j++) {
|
|
let timeMachine = thisObject.timeMachines[j]
|
|
if (timeMachine.onFocus === true) {
|
|
timeMachine.onKeyPressed(event)
|
|
}
|
|
}
|
|
}
|
|
|
|
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 - browserCanvas.width / TIME_MACHINE_WIDTH / 2
|
|
timeMachine.container.frame.position.y = browserCanvas.height / 2 - (browserCanvas.height - TOP_SPACE_HEIGHT - COCKPIT_SPACE_HEIGHT) / 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 <= TOP_SPACE_HEIGHT) {
|
|
thisObject.visible = false
|
|
if (thisObject.viewport !== undefined) {
|
|
thisObject.viewport.visible = false
|
|
}
|
|
} else {
|
|
thisObject.visible = true
|
|
if (thisObject.viewport !== undefined) {
|
|
thisObject.viewport.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() {
|
|
if (thisObject.visible === false) { return }
|
|
drawSpaceBackground()
|
|
|
|
for (let i = 0; i < thisObject.timeMachines.length; i++) {
|
|
let timeMachine = thisObject.timeMachines[thisObject.timeMachines.length - i - 1]
|
|
timeMachine.drawBackground()
|
|
}
|
|
}
|
|
|
|
function draw() {
|
|
if (canWeDraw === false) { return }
|
|
if (thisObject.visible === false) { 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()
|
|
}
|
|
}
|