374 lines
12 KiB
JavaScript
374 lines
12 KiB
JavaScript
/*
|
|
|
|
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 thisObject = {
|
|
container: undefined,
|
|
timeScale: undefined,
|
|
rateScale: undefined,
|
|
fitFunction: undefined,
|
|
timelineCharts: [],
|
|
physics: physics,
|
|
drawBackground: drawBackground,
|
|
draw: draw,
|
|
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
|
|
initialize: initialize,
|
|
finalize: finalize
|
|
}
|
|
|
|
const SEPARATION_BETWEEN_TIMELINE_CHARTS = 1.5
|
|
|
|
let timeLineCoordinateSystem = newTimeLineCoordinateSystem()
|
|
|
|
let mouse = {
|
|
position: {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
}
|
|
|
|
let syncWithDesignerLoop = 0
|
|
let timelineChartsMap = new Map()
|
|
|
|
setupContainer()
|
|
return thisObject
|
|
|
|
function setupContainer () {
|
|
thisObject.container = newContainer()
|
|
thisObject.container.initialize(MODULE_NAME)
|
|
thisObject.container.isDraggeable = true
|
|
thisObject.container.insideViewport = true
|
|
thisObject.container.detectMouseOver = true
|
|
|
|
thisObject.container.frame.width = TIME_MACHINE_WIDTH
|
|
thisObject.container.frame.height = TIME_MACHINE_HEIGHT
|
|
}
|
|
|
|
function finalize () {
|
|
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
|
let chart = thisObject.timelineCharts[i]
|
|
chart.finalize()
|
|
}
|
|
|
|
thisObject.timelineCharts = undefined
|
|
timelineChartsMap = undefined
|
|
|
|
if (thisObject.timeScale !== undefined) {
|
|
thisObject.timeScale.finalize()
|
|
thisObject.timeScale = undefined
|
|
}
|
|
if (thisObject.rateScale !== undefined) {
|
|
thisObject.rateScale.finalize()
|
|
thisObject.rateScale = undefined
|
|
}
|
|
|
|
thisObject.fitFunction = undefined
|
|
|
|
thisObject.container.finalize()
|
|
thisObject.container = undefined
|
|
|
|
mouse = undefined
|
|
}
|
|
|
|
function initialize (callBackFunction) {
|
|
recalculateScale()
|
|
|
|
/* Each Time Machine has a Time Scale and a Right Scale. */
|
|
|
|
thisObject.timeScale = newTimeScale()
|
|
thisObject.timeScale.fitFunction = thisObject.fitFunction
|
|
|
|
thisObject.timeScale.container.eventHandler.listenToEvent('Lenght Percentage Changed', function (event) {
|
|
thisObject.container.frame.width = TIME_MACHINE_WIDTH * event.lenghtPercentage / 100
|
|
recalculateScale()
|
|
moveToUserPosition(thisObject.container, timeLineCoordinateSystem, false, true, event.mousePosition, false, true)
|
|
thisObject.container.eventHandler.raiseEvent('Dimmensions Changed', event)
|
|
})
|
|
|
|
thisObject.timeScale.initialize()
|
|
|
|
thisObject.rateScale = newRateScale()
|
|
thisObject.rateScale.fitFunction = thisObject.fitFunction
|
|
|
|
thisObject.rateScale.container.eventHandler.listenToEvent('Height Percentage Changed', function (event) {
|
|
thisObject.container.frame.height = TIME_MACHINE_HEIGHT * event.heightPercentage / 100
|
|
recalculateScale()
|
|
moveToUserPosition(thisObject.container, timeLineCoordinateSystem, true, false, event.mousePosition)
|
|
thisObject.container.eventHandler.raiseEvent('Dimmensions Changed', event)
|
|
})
|
|
|
|
thisObject.rateScale.initialize()
|
|
|
|
thisObject.container.eventHandler.listenToEvent('onMouseOver', onMouseOver)
|
|
|
|
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE)
|
|
|
|
function onMouseOver (event) {
|
|
thisObject.timeScale.visible = true
|
|
thisObject.rateScale.visible = true
|
|
|
|
mouse.position.x = event.x
|
|
mouse.position.y = event.y
|
|
}
|
|
}
|
|
|
|
function getContainer (point, purpose) {
|
|
let container
|
|
|
|
if (thisObject.timeScale !== undefined) {
|
|
container = thisObject.timeScale.getContainer(point)
|
|
if (container !== undefined) {
|
|
if (container.isForThisPurpose(purpose)) {
|
|
return container
|
|
}
|
|
}
|
|
}
|
|
|
|
if (thisObject.rateScale !== undefined) {
|
|
container = thisObject.rateScale.getContainer(point)
|
|
if (container !== undefined) {
|
|
if (container.isForThisPurpose(purpose)) {
|
|
return container
|
|
}
|
|
}
|
|
}
|
|
|
|
for (let i = 0; i < this.timelineCharts.length; i++) {
|
|
container = this.timelineCharts[i].getContainer(point)
|
|
if (container !== undefined) {
|
|
if (container.isForThisPurpose(purpose)) {
|
|
if (thisObject.container.frame.isThisPointHere(point) === true) {
|
|
return container
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (thisObject.container.frame.isThisPointHere(point) === true) {
|
|
return thisObject.container
|
|
} else {
|
|
if (purpose === GET_CONTAINER_PURPOSE.MOUSE_OVER) {
|
|
thisObject.container.eventHandler.raiseEvent('onMouseNotOver')
|
|
if (thisObject.timeScale !== undefined) {
|
|
thisObject.timeScale.visible = false
|
|
}
|
|
if (thisObject.rateScale !== undefined) {
|
|
thisObject.rateScale.visible = false
|
|
}
|
|
}
|
|
return
|
|
}
|
|
}
|
|
|
|
function physics () {
|
|
thisObjectPhysics()
|
|
childrenPhysics()
|
|
syncWithDesigner()
|
|
}
|
|
|
|
function syncWithDesigner () {
|
|
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 chart 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.finalize()
|
|
timelineChartsMap.delete(timelineChart.nodeId)
|
|
timelineChart.payload = undefined
|
|
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 chart. */
|
|
timelineChart.container.connectToParent(thisObject.container, true, true, false, true, true, true)
|
|
timelineChart.container.fitFunction = thisObject.container.fitFunction
|
|
timelineChart.container.frame.height = thisObject.container.frame.height
|
|
timelineChart.container.frame.position.x = thisObject.container.frame.width / 2 - timelineChart.container.frame.width / 2
|
|
timelineChart.container.frame.position.y = 0
|
|
timelineChart.initialize(DEFAULT_EXCHANGE, DEFAULT_MARKET, timeLineCoordinateSystem, onTimelineChartInitialized)
|
|
|
|
function onTimelineChartInitialized (err) {
|
|
if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
|
|
if (ERROR_LOG === true) { logger.write('[ERROR] syncWithDesigner -> initializeTimelineChart -> Initialization Failed. -> Err ' + err.message) }
|
|
return
|
|
}
|
|
|
|
thisObject.timelineCharts.push(timelineChart)
|
|
timelineChart.payload.uiObject.setValue('')
|
|
}
|
|
}
|
|
}
|
|
|
|
function thisObjectPhysics () {
|
|
/* Screen Corner Date Calculation */
|
|
|
|
let point = {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
|
|
let cornerDate = getDateFromPoint(point, thisObject.container, timeLineCoordinateSystem)
|
|
cornerDate = new Date(cornerDate)
|
|
window.localStorage.setItem('Date @ Screen Corner', cornerDate.toUTCString())
|
|
}
|
|
|
|
function childrenPhysics () {
|
|
if (thisObject.timeScale === undefined) { return }
|
|
if (thisObject.rateScale === undefined) { return }
|
|
|
|
thisObject.timeScale.physics()
|
|
thisObject.rateScale.physics()
|
|
|
|
for (let i = 0; i < thisObject.timelineCharts.length; i++) {
|
|
let chart = thisObject.timelineCharts[i]
|
|
chart.physics()
|
|
}
|
|
|
|
/* Mouse Position Date Calculation */
|
|
|
|
let timePoint = {
|
|
x: mouse.position.x,
|
|
y: 0
|
|
}
|
|
|
|
let mouseDate = getDateFromPoint(timePoint, thisObject.container, timeLineCoordinateSystem)
|
|
|
|
thisObject.timeScale.date = new Date(mouseDate)
|
|
|
|
/* Mouse Position Rate Calculation */
|
|
|
|
let ratePoint = {
|
|
x: 0,
|
|
y: mouse.position.y
|
|
}
|
|
|
|
let mouseRate = getRateFromPoint(ratePoint, thisObject.container, timeLineCoordinateSystem)
|
|
|
|
thisObject.rateScale.rate = mouseRate
|
|
|
|
/* timeScale Positioning */
|
|
|
|
thisObject.timeScale.container.frame.position.x = mouse.position.x - thisObject.timeScale.container.frame.width / 2
|
|
|
|
timePoint = {
|
|
x: 0,
|
|
y: 0
|
|
}
|
|
|
|
timePoint = transformThisPoint(timePoint, thisObject.container.frame.container)
|
|
timePoint = thisObject.container.fitFunction(timePoint)
|
|
|
|
thisObject.timeScale.container.frame.position.y = timePoint.y
|
|
|
|
/* rateScale Positioning */
|
|
|
|
thisObject.rateScale.container.frame.position.y = mouse.position.y - thisObject.rateScale.container.frame.height / 2
|
|
|
|
ratePoint = {
|
|
x: thisObject.container.frame.width,
|
|
y: 0
|
|
}
|
|
|
|
ratePoint = transformThisPoint(ratePoint, thisObject.container.frame.container)
|
|
ratePoint = thisObject.container.fitFunction(ratePoint)
|
|
|
|
thisObject.rateScale.container.frame.position.x = ratePoint.x - thisObject.rateScale.container.frame.width
|
|
}
|
|
|
|
function drawBackground () {
|
|
thisBackground()
|
|
|
|
if (thisObject.container.frame.isInViewPort()) {
|
|
for (let i = 0; i < this.timelineCharts.length; i++) {
|
|
let chart = this.timelineCharts[i]
|
|
chart.drawBackground()
|
|
}
|
|
}
|
|
}
|
|
|
|
function draw () {
|
|
if (thisObject.container.frame.isInViewPort()) {
|
|
for (let i = 0; i < this.timelineCharts.length; i++) {
|
|
let chart = this.timelineCharts[i]
|
|
chart.draw()
|
|
}
|
|
|
|
if (thisObject.timeScale !== undefined) { thisObject.timeScale.draw() }
|
|
if (thisObject.rateScale !== undefined) { thisObject.rateScale.draw() }
|
|
|
|
thisObject.container.frame.draw(false, true, false, thisObject.fitFunction)
|
|
}
|
|
}
|
|
|
|
function thisBackground () {
|
|
return
|
|
let params = {
|
|
cornerRadius: 15,
|
|
lineWidth: 5,
|
|
opacity: 1,
|
|
container: thisObject.container,
|
|
borderColor: UI_COLOR.RUSTED_RED,
|
|
backgroundColor: UI_COLOR.WHITE,
|
|
fitFunction: thisObject.fitFunction,
|
|
coordinateSystem: timeLineCoordinateSystem
|
|
}
|
|
|
|
roundedCornersBackground(params)
|
|
}
|
|
|
|
function recalculateScale () {
|
|
let minValue = {
|
|
x: MIN_PLOTABLE_DATE.valueOf(),
|
|
y: 0
|
|
}
|
|
|
|
let maxValue = {
|
|
x: MAX_PLOTABLE_DATE.valueOf(),
|
|
y: nextPorwerOf10(USDT_BTC_HTH) / 4
|
|
}
|
|
|
|
timeLineCoordinateSystem.initialize(
|
|
minValue,
|
|
maxValue,
|
|
thisObject.container.frame.width,
|
|
thisObject.container.frame.height
|
|
)
|
|
}
|
|
}
|