Files
Superalgos/ChartsSpace/TimeMachine.js
T

382 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 charts 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 charts, 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,
charts: [],
physics: physics,
drawBackground: drawBackground,
draw: draw,
getContainer: getContainer, // returns the inner most container that holds the point received by parameter.
initialize: initialize,
finalize: finalize
}
thisObject.container = newContainer()
thisObject.container.initialize(MODULE_NAME)
thisObject.container.isDraggeable = false
thisObject.container.detectMouseOver = true
thisObject.container.frame.width = TIME_MACHINE_WIDTH
thisObject.container.frame.height = TIME_MACHINE_HEIGHT
thisObject.container.frame.position.x = browserCanvas.width / 2 - TIME_MACHINE_WIDTH / 2
thisObject.container.frame.position.y = browserCanvas.height / 2 - TIME_MACHINE_HEIGHT / 2
let controlPanelHandle // We need this to destroy the Panel when this object is itself destroyed or no longer needs it...
// ... also to request a reference to the object for the cases we need it.
const SEPARATION_BETWEEN_TIMELINE_CHARTS = 1.5
let timeLineCoordinateSystem = newTimeLineCoordinateSystem()
let mouse = {
position: {
x: 0,
y: 0
}
}
return thisObject
function finalize () {
for (let i = 0; i < thisObject.charts.length; i++) {
let chart = thisObject.charts[i]
chart.finalize()
}
thisObject.container.finalize()
thisObject.container = undefined
thisObject.timeScale.finalize()
thisObject.timeScale = undefined
thisObject.rateScale.finalize()
thisObject.rateScale = undefined
thisObject.fitFunction = undefined
}
function initialize (callBackFunction) {
/* Each Time Machine has a Control Panel. */
let panelOwner = 'Global'
controlPanelHandle = canvas.panelsSpace.createNewPanel('Time Control Panel', undefined, panelOwner)
let controlPanel = canvas.panelsSpace.getPanel(controlPanelHandle, panelOwner)
recalculateScale()
/* First, we initialize the market that we are going to show first on screen. Later all the other markets will be initialized on the background. */
let position = 0 // This defines the position of each chart respect to each other.
let timelineChart = newTimelineChart()
timelineChart.container.connectToParent(thisObject.container, true, true, false, true, true, true)
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 = timelineChart.container.frame.height * SEPARATION_BETWEEN_TIMELINE_CHARTS * position
position++
timelineChart.initialize(DEFAULT_EXCHANGE, DEFAULT_MARKET, timeLineCoordinateSystem, onDefaultInitialized)
function onDefaultInitialized (err) {
if (err.result !== GLOBAL.DEFAULT_OK_RESPONSE.result) {
callBackFunction(err)
return
}
thisObject.charts.push(timelineChart)
controlPanel.container.eventHandler.listenToEvent('Datetime Changed', timelineChart.setDatetime, undefined)
timelineChart.container.eventHandler.listenToEvent('Datetime Changed', controlPanel.setDatetime)
/* 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)
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)
initializeTheRest()
}
function onMouseOver (event) {
thisObject.timeScale.visible = true
thisObject.rateScale.visible = true
mouse.position.x = event.x
mouse.position.y = event.y
}
function initializeTheRest () {
let leftToInitialize = SUPPORTED_EXCHANGES.length * SUPPORTED_MARKETS.length - 1 // The default exchange and market was already initialized.
let alreadyInitialized = 0
if (alreadyInitialized === leftToInitialize) {
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE)
return
}
for (let i = 0; i < SUPPORTED_EXCHANGES.length; i++) {
for (let j = 0; j < SUPPORTED_MARKETS.length; j++) {
let exchange = SUPPORTED_EXCHANGES[i]
let market = SUPPORTED_MARKETS[j]
if (
exchange === DEFAULT_EXCHANGE &&
market.assetA === DEFAULT_MARKET.assetA &&
market.assetB === DEFAULT_MARKET.assetB
) { continue }
initializeTimelineChart(exchange, market)
}
}
function initializeTimelineChart (exchange, market) {
let timelineChart = newTimelineChart()
timelineChart.container.connectToParent(thisObject.container, true, true, false, true, true, true)
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 = timelineChart.container.frame.height * SEPARATION_BETWEEN_TIMELINE_CHARTS * position
position++
timelineChart.initialize(exchange, market, timeLineCoordinateSystem, finalSteps)
function finalSteps () {
thisObject.charts.push(timelineChart)
controlPanel.container.eventHandler.listenToEvent('Datetime Changed', timelineChart.setDatetime, undefined)
timelineChart.container.eventHandler.listenToEvent('Datetime Changed', controlPanel.setDatetime)
alreadyInitialized++
if (alreadyInitialized === leftToInitialize) {
callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE)
}
}
}
}
}
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.charts.length; i++) {
container = this.charts[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')
thisObject.timeScale.visible = false
thisObject.rateScale.visible = false
}
return
}
}
function physics () {
thisObjectPhysics()
childrenPhysics()
}
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 () {
thisObject.timeScale.physics()
thisObject.rateScale.physics()
for (let i = 0; i < thisObject.charts.length; i++) {
let chart = thisObject.charts[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: 10000000000000,
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.charts.length; i++) {
let chart = this.charts[i]
chart.drawBackground()
}
}
}
function draw () {
if (thisObject.container.frame.isInViewPort()) {
for (let i = 0; i < this.charts.length; i++) {
let chart = this.charts[i]
chart.draw()
}
if (thisObject.timeScale !== undefined) { thisObject.timeScale.draw() }
if (thisObject.rateScale !== undefined) { thisObject.rateScale.draw() }
// thisObject.container.frame.draw(false, true, false)
}
}
function thisBackground () {
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
)
}
}