460 lines
15 KiB
JavaScript
460 lines
15 KiB
JavaScript
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,
|
|
setTimeFrame: setTimeFrame,
|
|
physics: physics,
|
|
draw: draw,
|
|
drawBackground: drawBackground,
|
|
drawForeground: drawForeground,
|
|
getContainer: getContainer,
|
|
initialize: initialize,
|
|
finalize: finalize
|
|
}
|
|
|
|
let timeMachineCoordinateSystem
|
|
let timelineChartCoordinateSystem = newCoordinateSystem()
|
|
|
|
let layersPanel
|
|
|
|
let layersPanelHandle
|
|
|
|
let onViewportPositionChangedEventSuscriptionId
|
|
let onViewportZoomChangedEventSuscriptionId
|
|
let onMouseOverEventSuscriptionId
|
|
let onMouseNotOverEventSuscriptionId
|
|
let timeFrameScaleEventSuscriptionId
|
|
let timeFrameScaleMouseOverEventSuscriptionId
|
|
|
|
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 () {
|
|
if (thisObject.layersManager !== undefined) {
|
|
finalizeLayersManager()
|
|
}
|
|
|
|
if (thisObject.rateScale !== undefined) {
|
|
finalizeRateScale()
|
|
}
|
|
|
|
if (thisObject.timeFrameScale !== undefined) {
|
|
finalizeTimeFrameScale()
|
|
}
|
|
|
|
canvas.chartSpace.viewport.eventHandler.stopListening(onViewportPositionChangedEventSuscriptionId)
|
|
canvas.chartSpace.viewport.eventHandler.stopListening(onViewportZoomChangedEventSuscriptionId)
|
|
thisObject.container.eventHandler.stopListening(onMouseOverEventSuscriptionId)
|
|
thisObject.container.eventHandler.stopListening(onMouseNotOverEventSuscriptionId)
|
|
|
|
thisObject.container.finalize()
|
|
thisObject.container = undefined
|
|
|
|
thisObject.payload = undefined
|
|
mouse = undefined
|
|
logger = 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(rateScaleEventSuscriptionId)
|
|
thisObject.rateScale.container.eventHandler.stopListening(rateScaleMouseOverEventSuscriptionId)
|
|
thisObject.rateScale.finalize()
|
|
thisObject.rateScale = undefined
|
|
|
|
/* Resets the local container with the dimessions of its parent, the Time Machine */
|
|
thisObject.container.frame.position.position.y = 0
|
|
thisObject.container.frame.position.height = thisObject.container.parentContainer.frame.height
|
|
}
|
|
|
|
function initialize (pTimeMachineCoordinateSystem) {
|
|
/* We load the logow we will need for the background. */
|
|
|
|
timeMachineCoordinateSystem = pTimeMachineCoordinateSystem
|
|
|
|
timeFrame = INITIAL_TIME_PERIOD
|
|
datetime = NEW_SESSION_INITIAL_DATE
|
|
|
|
/* Event Subscriptions - we need this events to be fired first here and then in active Plotters. */
|
|
onViewportPositionChangedEventSuscriptionId = canvas.chartSpace.viewport.eventHandler.listenToEvent('Position Changed', onViewportPositionChanged)
|
|
onViewportZoomChangedEventSuscriptionId = canvas.chartSpace.viewport.eventHandler.listenToEvent('Zoom Changed', onViewportZoomChanged)
|
|
|
|
onMouseOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
|
onMouseNotOverEventSuscriptionId = thisObject.container.eventHandler.listenToEvent('onMouseNotOver', onMouseNotOver)
|
|
}
|
|
|
|
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, DEFAULT_EXCHANGE, DEFAULT_MARKET)
|
|
thisObject.plotterManager.setTimeFrame(timeFrame)
|
|
}
|
|
|
|
function initializeRateScale () {
|
|
thisObject.rateScale = newRateScale()
|
|
thisObject.rateScale.fitFunction = thisObject.fitFunction
|
|
thisObject.rateScale.payload = thisObject.payload.node.rateScale.payload
|
|
|
|
rateScaleEventSuscriptionId = thisObject.rateScale.container.eventHandler.listenToEvent('Height Percentage Changed', function (event) {
|
|
thisObject.container.frame.height = TIME_MACHINE_HEIGHT * event.value / 100
|
|
recalculateCoordinateSystem()
|
|
if (event.isUserAction === true) {
|
|
moveToUserPosition(thisObject.container, timelineChartCoordinateSystem, true, false, event.mousePosition, false, true)
|
|
}
|
|
thisObject.container.eventHandler.raiseEvent('Dimmensions Changed', event)
|
|
})
|
|
|
|
thisObject.rateScale.initialize(timelineChartCoordinateSystem, thisObject.container.parentContainer)
|
|
|
|
rateScaleMouseOverEventSuscriptionId = thisObject.rateScale.container.eventHandler.listenToEvent('onMouseOverScale', rateScaleMouseOver)
|
|
|
|
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 Changed', function (event) {
|
|
let currentTimeFrame = timeFrame
|
|
timeFrame = event.timeFrame
|
|
if (timeFrame !== currentTimeFrame) {
|
|
if (thisObject.plotterManager !== undefined) {
|
|
thisObject.plotterManager.setTimeFrame(timeFrame)
|
|
}
|
|
}
|
|
})
|
|
|
|
thisObject.timeFrameScale.initialize(timeMachineCoordinateSystem, thisObject.container.parentContainer)
|
|
|
|
timeFrameScaleMouseOverEventSuscriptionId = thisObject.timeFrameScale.container.eventHandler.listenToEvent('onMouseOverScale', timeFrameScaleMouseOver)
|
|
|
|
function timeFrameScaleMouseOver (event) {
|
|
thisObject.container.eventHandler.raiseEvent('onChildrenMouseOver', event)
|
|
|
|
saveUserPosition(thisObject.container, timeMachineCoordinateSystem, event)
|
|
}
|
|
}
|
|
|
|
function setTimeFrame (pTimeFrame) {
|
|
timeFrame = pTimeFrame
|
|
if (thisObject.plotterManager !== undefined) {
|
|
thisObject.plotterManager.setTimeFrame(timeFrame)
|
|
}
|
|
}
|
|
|
|
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
|
|
|
|
saveUserPosition(thisObject.container, timeMachineCoordinateSystem, event)
|
|
}
|
|
|
|
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 onViewportZoomChanged (event) {
|
|
if (thisObject.container.frame.isInViewPort()) {
|
|
recalculateCurrentDatetime()
|
|
}
|
|
}
|
|
|
|
function onViewportPositionChanged () {
|
|
if (thisObject.container.frame.isInViewPort()) {
|
|
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.chartSpace.viewport.visibleArea.bottomRight.x - canvas.chartSpace.viewport.visibleArea.bottomLeft.x) / 2,
|
|
y: (canvas.chartSpace.viewport.visibleArea.bottomRight.y - canvas.chartSpace.viewport.visibleArea.topRight.y) / 2
|
|
}
|
|
|
|
center = unTransformThisPoint(center, thisObject.container)
|
|
if (thisObject.rateScale === undefined) {
|
|
center = timeMachineCoordinateSystem.unInverseTransform(center, thisObject.container.frame.height)
|
|
} else {
|
|
center = timelineChartCoordinateSystem.unInverseTransform(center, thisObject.container.frame.height)
|
|
}
|
|
|
|
let newDate = new Date(0)
|
|
newDate.setUTCSeconds(center.x / 1000)
|
|
|
|
datetime = newDate
|
|
|
|
thisObject.plotterManager.setDatetime(datetime)
|
|
}
|
|
|
|
function getContainer (point, purpose) {
|
|
let container
|
|
|
|
if (thisObject.rateScale !== undefined) {
|
|
container = thisObject.rateScale.getContainer(point)
|
|
if (container !== undefined) {
|
|
if (container.isForThisPurpose(purpose)) {
|
|
return container
|
|
}
|
|
}
|
|
}
|
|
|
|
if (thisObject.timeFrameScale !== undefined) {
|
|
container = thisObject.timeFrameScale.getContainer(point)
|
|
if (container !== undefined) {
|
|
if (container.isForThisPurpose(purpose)) {
|
|
return container
|
|
}
|
|
}
|
|
}
|
|
|
|
return container
|
|
}
|
|
|
|
function physics () {
|
|
thisObjectPhysics()
|
|
childrenPhysics()
|
|
syncWithDesignerScales()
|
|
syncWithDesignerLayersManager()
|
|
}
|
|
|
|
function thisObjectPhysics () {
|
|
|
|
}
|
|
|
|
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()
|
|
}
|
|
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 childrenPhysics () {
|
|
if (thisObject.rateScale !== undefined) {
|
|
thisObject.rateScale.physics()
|
|
}
|
|
if (thisObject.timeFrameScale !== undefined) {
|
|
thisObject.timeFrameScale.physics()
|
|
}
|
|
}
|
|
|
|
function tooTiny () {
|
|
if (canvas.chartSpace.viewport.zoomLevel < Math.trunc(-28.25 * 100) / 100) {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
function tooSmall () {
|
|
if (canvas.chartSpace.viewport.zoomLevel < Math.trunc(-27.25 * 100) / 100) {
|
|
return true
|
|
} else {
|
|
return false
|
|
}
|
|
}
|
|
|
|
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.draw() }
|
|
if (thisObject.rateScale !== undefined) { thisObject.rateScale.draw() }
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawForeground () {
|
|
if (thisObject.container.frame.isInViewPort()) {
|
|
if (drawScales === true) {
|
|
if (thisObject.timeFrameScale !== undefined) { thisObject.timeFrameScale.drawForeground() }
|
|
if (thisObject.rateScale !== undefined) { thisObject.rateScale.drawForeground() }
|
|
}
|
|
}
|
|
}
|
|
|
|
function drawChartsBackground () {
|
|
/* We will paint some transparent background here. */
|
|
|
|
let opacity = '0.1'
|
|
|
|
let fromPoint = {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
|
|
let toPoint = {
|
|
x: thisObject.container.frame.width,
|
|
y: thisObject.container.frame.height
|
|
}
|
|
|
|
fromPoint = transformThisPoint(fromPoint, thisObject.container)
|
|
toPoint = transformThisPoint(toPoint, thisObject.container)
|
|
|
|
fromPoint = thisObject.fitFunction(fromPoint)
|
|
toPoint = thisObject.fitFunction(toPoint)
|
|
|
|
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()
|
|
}
|
|
|
|
function recalculateCoordinateSystem () {
|
|
let minValue = {
|
|
x: MIN_PLOTABLE_DATE.valueOf(),
|
|
y: 0
|
|
}
|
|
|
|
let maxValue = {
|
|
x: MAX_PLOTABLE_DATE.valueOf(),
|
|
y: nextPorwerOf10(USDT_BTC_HTH) / 4
|
|
}
|
|
|
|
timelineChartCoordinateSystem.initialize(
|
|
minValue,
|
|
maxValue,
|
|
thisObject.container.frame.width,
|
|
thisObject.container.frame.height
|
|
)
|
|
}
|
|
}
|