diff --git a/.gitignore b/.gitignore index 5856c6c68..f1d32b8c8 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,7 @@ .vs/* -.obj/* - +obj/* +bin/* node_modules/* this.server.config.json diff --git a/AABrowserAPI/BotCode.js b/AABrowserAPI/BotCode.js new file mode 100644 index 000000000..c8f2bf687 --- /dev/null +++ b/AABrowserAPI/BotCode.js @@ -0,0 +1,61 @@ +exports.newBotCode = function newBotCode() { + + let thisObject = { + saveBotCode: saveBotCode, + initialize: initialize + } + + let storage; + + return thisObject; + + function initialize(pServerConfig) { + + const STORAGE = require('../Server/Storage'); + storage = STORAGE.newStorage(); + + storage.initialize(undefined, pServerConfig); + + } + + function saveBotCode(pDevTeam, pSource, pRepo, pPath, pSourceCode, callBackFunction) { + + let blobText = nodify(pSourceCode.toString()); + + storage.writeData(pDevTeam + "/" + pSource, pRepo, pPath, blobText, onDataWritten); + + function onDataWritten(err) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] API -> saveBotCode -> Source code file could not be saved. "); + console.log("[ERROR] API -> saveBotCode -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } + + function nodify(pData) { + + let start = pData.indexOf('function') + 9; + let end = pData.indexOf('(') + 0; + + let functionName = pData.substring(start, end); + + let result = "exports." + functionName + " = " + pData; + + result = replaceAll(result, "webRequire", "require"); + result = replaceAll(result, "window", "global"); + + return result; + + function replaceAll(text, search, replacement) { + return text.replace(new RegExp(search, 'g'), replacement); + } + } + +} \ No newline at end of file diff --git a/AABrowserAPI/TeamSetup.js b/AABrowserAPI/TeamSetup.js new file mode 100644 index 000000000..b3f3cc437 --- /dev/null +++ b/AABrowserAPI/TeamSetup.js @@ -0,0 +1,1149 @@ +exports.newTeamSetup = function newTeamSetup() { + + let thisObject = { + newTeam: newTeam, + deleteTeam: deleteTeam, + initialize: initialize + } + + const MASTER_APP_API = 'https://app-api.advancedalgos.net/graphql'; + + let storage; + + return thisObject; + + function initialize(pServerConfig) { + + const STORAGE = require('../Server/Storage'); + storage = STORAGE.newStorage(); + + storage.initialize(undefined, pServerConfig); + } + + function newTeam(pTeamCodeName, pTeamDisplayName, pUserName, pBotCodeName, pBotDisplayName, pUserId, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> Entering function."); } + + createContainer(); + + function createContainer() { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> createContainer -> Entering function."); } + + storage.createContainer(pTeamCodeName, onContainerCreated); + + function onContainerCreated(err) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> createContainer -> onContainerCreated -> Could not create the container. "); + console.log("[ERROR] TeamSetup -> newTeam -> createContainer -> onContainerCreated -> err.message = " + err.message); + + if (err.message === "ContainerAlreadyExists") { + + let error = { + result: err.message, + message: "Team Name already taken" + }; + + callBackFunction(error); + + } else { + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + return; + } + + forkBotCode(); + } + } + + function forkBotCode() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkBotCode -> Entering function."); } + + storage.readData("AATemplate", "bots/BotName-Trading-Bot/Trading-Process", "User.Bot.js", false, onDataRead); + + function onDataRead(err, pFileContent) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> Could not read a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let team = pTeamCodeName; + let filePath = "bots" + "/" + pBotCodeName + "-Trading-Bot/Trading-Process"; + let fileName = "User.Bot.js"; + + storage.writeData(team, filePath, fileName, pFileContent, onDataWritten); + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let team = pTeamCodeName; + let filePath = "members" + "/" + pUserName + "/" + pBotCodeName + "-Trading-Bot/Trading-Process"; + let fileName = "User.Bot.js"; + + storage.writeData(team, filePath, fileName, pFileContent, onDataWritten); + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> onDataWritten -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> onDataWritten -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + forkBotConfig(); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> onDataRead -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotCode -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + function forkBotConfig() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkBotConfig -> Entering function."); } + + storage.readData("AATemplate", "bots/BotName-Trading-Bot", "this.bot.config.json", false, onDataRead); + + function onDataRead(err, pFileContent) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> Could not read a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let botConfig = JSON.parse(pFileContent); + + /* + + The first thing we are going to replace is the status dependency to iteself. Each trading bots reads its own status report and its + dependency can be located anywhere within the status dependency array. We will scan the array for the self dependency and replace + that first with the name of the new bot. After that wi will continue replacing the rest of the information. + */ + + for (let i = 0; i < botConfig.processes[0].statusDependencies.length; i++) { + + if ( + botConfig.processes[0].statusDependencies[i].devTeam === botConfig.devTeam && + botConfig.processes[0].statusDependencies[i].bot === botConfig.codeName + ) { + + botConfig.processes[0].statusDependencies[i].devTeam = pTeamCodeName; + botConfig.processes[0].statusDependencies[i].bot = pBotCodeName; + + } + } + + botConfig.displayName = pBotDisplayName; + botConfig.codeName = pBotCodeName; + botConfig.devTeam = pTeamCodeName; + botConfig.profilePicture = pBotCodeName + ".png"; + + let fileContent = JSON.stringify(botConfig); + + let team = pTeamCodeName; + let filePath = "bots" + "/" + pBotCodeName + "-Trading-Bot"; + let fileName = "this.bot.config.json"; + + storage.writeData(team, filePath, fileName, fileContent, onDataWritten); + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let team = pTeamCodeName; + let filePath = "members" + "/" + pUserName + "/" + pBotCodeName + "-Trading-Bot"; + let fileName = "this.bot.config.json"; + + storage.writeData(team, filePath, fileName, fileContent, onDataWritten); + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> onDataWritten -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> onDataWritten -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + forkAACloud(); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> onDataRead -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkBotConfig -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + function forkAACloud() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkAACloud -> Entering function."); } + + storage.readData("AATemplate", "AACloud", "this.config.json", false, onDataRead); + + function onDataRead(err, pFileContent) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> Could not read a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let botConfig = JSON.parse(pFileContent); + + botConfig.executionList[0].devTeam = pTeamCodeName; + botConfig.executionList[0].bot = pBotCodeName; + botConfig.executionList[0].repo = pBotCodeName + "-Trading-Bot"; + + let fileContent = JSON.stringify(botConfig); + + let team = pTeamCodeName; + let filePath = "AACloud"; + let fileName = "this.config.json"; + + storage.writeData(team, filePath, fileName, fileContent, onDataWritten); + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let team = pTeamCodeName; + let filePath = "members" + "/" + pUserName + "/" + "AACloud"; + let fileName = "this.config.json"; + + storage.writeData(team, filePath, fileName, fileContent, onDataWritten); + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> onDataWritten -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> onDataWritten -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + addToSessions(); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> onDataRead -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> forkAACloud -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + + } + + function addToSessions() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> addToSessions -> Entering function."); } + + storage.readData("AdvancedAlgos", "AAPlatform", "open.sessions.json", false, onDataRead); + + function onDataRead(err, pFileContent) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> addToSessions -> onDataRead -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToSessions -> onDataRead -> Could not read a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> addToSessions -> onDataRead -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + pFileContent = pFileContent.trim(); + let sessions = JSON.parse(pFileContent); + let token = createRandomToken(); + + function createRandomToken() { + + let text = ""; + let possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"; + + for (var i = 0; i < 512; i++) + text += possible.charAt(Math.floor(Math.random() * possible.length)); + + return text; + } + + let newSession = { + sessionToken: token, + userName: pUserName, + devTeams: [ + { + codeName: pTeamCodeName, + displayName: pTeamDisplayName, + userBots: [ + { + displayName: pBotDisplayName, + codeName: pBotCodeName, + type: "Trading", + repo: pBotCodeName + "-Trading-Bot", + processes: [ + { + name: "Trading-Process" + } + ] + } + ], + devTeamDependencies: ["AAMasters", "AAVikings"] + } + ], + exchangeKeys: [ + { + exchange: "Poloniex", + key: "", + secret: "" + } + ] + }; + + sessions.push(newSession); + + let fileContent = JSON.stringify(sessions); + + storage.writeData("AdvancedAlgos", "AAPlatform", "open.sessions.json", fileContent, onDataWritten); + + addToUserModuleDatabase(token); // NOTE that this function is not properly inserted on the sequence, so if it fails noone will know. + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> addToSessions -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToSessions -> onDataRead -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> addToSessions -> onDataRead -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + addToEcosystem(); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToSessions -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToSessions -> onDataRead -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToSessions -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + function addToUserModuleDatabase(pSessionToken) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> addToUserModuleDatabase -> Entering function."); } + + + const graphqlClient = require('graphql-client') + + const usersModuleAPI = graphqlClient({ + url: MASTER_APP_API + }); + + + let variables = { + userId: pUserId, + sessionToken: pSessionToken + }; + + usersModuleAPI.query(` + mutation($userId: String, $sessionToken: String){ + users_UpdateSessionToken(userId: $userId, sessionToken: $sessionToken){ + id + alias + } + } + `, variables, function (req, res) { + if (res.status === 401) { + console.log('[ERROR] TeamSetup -> newTeam -> Error trying to save the session token at the Users Module'); + console.log('[ERROR] TeamSetup -> newTeam -> res.status = 401'); + } + }).then(res => { + if (res.errors) { + console.log('[ERROR] TeamSetup -> newTeam -> Error trying to save the session token at the Users Module'); + console.log('[ERROR] TeamSetup -> newTeam -> res.errors = ' + res.errors); + } + }).catch(error => { + console.log('Error trying to save the session token at the Users Module'); + console.log('[ERROR] TeamSetup -> newTeam -> errors = ' + errors); + }); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToUserModuleDatabase -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + function addToEcosystem() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> addToEcosystem -> Entering function."); } + + storage.readData("AdvancedAlgos", "AAPlatform", "ecosystem.json", false, onDataRead); + + function onDataRead(err, pFileContent) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> addToEcosystem -> onDataRead -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToEcosystem -> onDataRead -> Could not read a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> addToEcosystem -> onDataRead -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + pFileContent = pFileContent.trim(); + let ecosystem = JSON.parse(pFileContent); + + let newTeam = { + codeName: pTeamCodeName, + displayName: pTeamDisplayName, + bots: [ + { + "repo": pBotCodeName + "-Trading-Bot", + "configFile": "this.bot.config.json" + } + ], + "plotters": [] + }; + + ecosystem.devTeams.push(newTeam); + + let fileContent = JSON.stringify(ecosystem); + + storage.writeData("AdvancedAlgos", "AAPlatform", "ecosystem.json", fileContent, onDataWritten); + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> newTeam -> addToEcosystem -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToEcosystem -> onDataRead -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> newTeam -> addToEcosystem -> onDataRead -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToEcosystem -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToEcosystem -> onDataRead -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> addToEcosystem -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> newTeam -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + function deleteTeam(pTeamCodeName, pUserName, pBotCodeName, pUserId, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> Entering function."); } + + deleteContainer(); + + function deleteContainer() { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteContainer -> Entering function."); } + + storage.deleteContainer(pTeamCodeName, onContainerDeleted); + + function onContainerDeleted(err) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteContainer -> onContainerDeleted -> Could not delete the container. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteContainer -> onContainerDeleted -> err.message = " + err.message); + + if (err.message === "ContainerNotFound") { + + let error = { + result: err.result, + message: "Team does not exist" + }; + + deleteBotCode(); // Even if the container does not exist we will try to delete the rest of the stuff related to the team. + + } else { + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + return; + } + + deleteBotCode(); + } + } + + function deleteBotCode() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteBotCode -> Entering function."); } + + let team = pTeamCodeName; + let filePath = "bots" + "/" + pBotCodeName + "-Trading-Bot/Trading-Process"; + let fileName = "User.Bot.js"; + + storage.deleteBlob(team, filePath, fileName, onBlobDeleted); + + function onBlobDeleted(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteBotCode -> onBlobDeleted -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotCode -> onBlobDeleted -> Could not delete a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotCode -> onBlobDeleted -> err.message = " + err.message); + + // Even if this failed we will try to delete the rest of the stuff related to the team. + } + + let team = pTeamCodeName; + let filePath = "members" + "/" + pUserName + "/" + pBotCodeName + "-Trading-Bot/Trading-Process"; + let fileName = "User.Bot.js"; + + storage.deleteBlob(team, filePath, fileName, onBlobDeleted); + + function onBlobDeleted(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteBotCode -> onBlobDeleted -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotCode -> onBlobDeleted -> onBlobDeleted -> Could not delete a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotCode -> onBlobDeleted -> onBlobDeleted -> err.message = " + err.message); + + // Even if this failed we will try to delete the rest of the stuff related to the team. + } + + deleteBotConfig(); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotCode -> onBlobDeleted -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotCode -> onBlobDeleted -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotCode -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + + } + + function deleteBotConfig() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteBotConfig -> Entering function."); } + + let team = pTeamCodeName; + let filePath = "bots" + "/" + pBotCodeName + "-Trading-Bot"; + let fileName = "this.bot.config.json"; + + storage.deleteBlob(team, filePath, fileName, onBlobDeleted); + + function onBlobDeleted(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteBotConfig -> onBlobDeleted -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotConfig -> onBlobDeleted -> Could not delete a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotConfig -> onBlobDeleted -> err.message = " + err.message); + + // Even if this failed we will try to delete the rest of the stuff related to the team. + } + + let team = pTeamCodeName; + let filePath = "members" + "/" + pUserName + "/" + pBotCodeName + "-Trading-Bot"; + let fileName = "this.bot.config.json"; + + storage.deleteBlob(team, filePath, fileName, onBlobDeleted); + + function onBlobDeleted(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteBotConfig -> onBlobDeleted -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotConfig -> onBlobDeleted -> onBlobDeleted -> Could not delete a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotConfig -> onBlobDeleted -> onBlobDeleted -> err.message = " + err.message); + + // Even if this failed we will try to delete the rest of the stuff related to the team. + } + + deleteAACloud(); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotConfig -> onBlobDeleted -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotConfig -> onBlobDeleted -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteBotConfig -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + + } + + function deleteAACloud() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteAACloud -> Entering function."); } + + let team = pTeamCodeName; + let filePath = "AACloud"; + let fileName = "this.config.json"; + + storage.deleteBlob(team, filePath, fileName, onBlobDeleted); + + function onBlobDeleted(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteAACloud -> onBlobDeleted -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteAACloud -> onBlobDeleted -> Could not delete a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteAACloud -> onBlobDeleted -> err.message = " + err.message); + + // Even if this failed we will try to delete the rest of the stuff related to the team. + } + + let team = pTeamCodeName; + let filePath = "members" + "/" + pUserName + "/" + "AACloud"; + let fileName = "this.config.json"; + + storage.deleteBlob(team, filePath, fileName, onBlobDeleted); + + function onBlobDeleted(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> deleteAACloud -> onBlobDeleted -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteAACloud -> onBlobDeleted -> onBlobDeleted -> Could not delete a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteAACloud -> onBlobDeleted -> onBlobDeleted -> err.message = " + err.message); + + // Even if this failed we will try to delete the rest of the stuff related to the team. + } + + removeFromSessions(); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteAACloud -> onBlobDeleted -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteAACloud -> onBlobDeleted -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> deleteAACloud -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + + } + + function removeFromSessions() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> removeFromSessions -> Entering function."); } + + storage.readData("AdvancedAlgos", "AAPlatform", "open.sessions.json", false, onDataRead); + + function onDataRead(err, pFileContent) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> removeFromSessions -> onDataRead -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromSessions -> onDataRead -> Could not read a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromSessions -> onDataRead -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + pFileContent = pFileContent.trim(); + let sessions = JSON.parse(pFileContent); + + for (let i = 0; i < sessions.length; i++) { + + let session = sessions[i]; + + if (session.userName === pUserName && session.devTeams[0].codeName === pTeamCodeName) { + + // Found it! Deleting... + + sessions.splice(i, 1); + break; + } + + } + + let fileContent = JSON.stringify(sessions); + + storage.writeData("AdvancedAlgos", "AAPlatform", "open.sessions.json", fileContent, onDataWritten); + + removeFromUserModuleDatabase(""); // NOTE that this function is not properly inserted on the sequence, so if it fails noone will know. + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> removeFromSessions -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromSessions -> onDataRead -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromSessions -> onDataRead -> onDataWritten -> err.message = " + err.message); + + // Even if this failed we will try to delete the rest of the stuff related to the team. + } + + removeFromEcosystem(); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromSessions -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromSessions -> onDataRead -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromSessions -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + function removeFromUserModuleDatabase(pSessionToken) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> removeFromUserModuleDatabase -> Entering function."); } + + + const graphqlClient = require('graphql-client') + + const usersModuleAPI = graphqlClient({ + url: MASTER_APP_API + }); + + + let variables = { + userId: pUserId, + sessionToken: pSessionToken + }; + + usersModuleAPI.query(` + mutation($userId: String, $sessionToken: String){ + users_UpdateSessionToken(userId: $userId, sessionToken: $sessionToken){ + id + alias + } + } + `, variables, function (req, res) { + if (res.status === 401) { + console.log('[ERROR] TeamSetup -> deleteTeam -> Error trying to delete the session token at the Users Module'); + console.log('[ERROR] TeamSetup -> deleteTeam -> res.status = 401'); + } + }).then(res => { + if (res.errors) { + console.log('[ERROR] TeamSetup -> deleteTeam -> Error trying to delete the session token at the Users Module'); + console.log('[ERROR] TeamSetup -> deleteTeam -> res.errors = ' + res.errors); + } + }).catch(error => { + console.log('Error trying to delete the session token at the Users Module'); + console.log('[ERROR] TeamSetup -> deleteTeam -> errors = ' + errors); + }); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromUserModuleDatabase -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + function removeFromEcosystem() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> removeFromEcosystem -> Entering function."); } + + storage.readData("AdvancedAlgos", "AAPlatform", "ecosystem.json", false, onDataRead); + + function onDataRead(err, pFileContent) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> removeFromEcosystem -> onDataRead -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromEcosystem -> onDataRead -> Could not read a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromEcosystem -> onDataRead -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + pFileContent = pFileContent.trim(); + let ecosystem = JSON.parse(pFileContent); + + for (let i = 0; i < ecosystem.devTeams.length; i++) { + + let team = ecosystem.devTeams[i]; + + if (team.codeName === pTeamCodeName) { + + // Found! Deleting it... + + ecosystem.devTeams.splice(i, 1); + break; + } + } + + let fileContent = JSON.stringify(ecosystem); + + storage.writeData("AdvancedAlgos", "AAPlatform", "ecosystem.json", fileContent, onDataWritten); + + function onDataWritten(err) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] TeamSetup -> deleteTeam -> removeFromEcosystem -> onDataRead -> onDataWritten -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromEcosystem -> onDataRead -> onDataWritten -> Could not write a file. "); + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromEcosystem -> onDataRead -> onDataWritten -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromEcosystem -> onDataRead -> onDataWritten -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromEcosystem -> onDataRead -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> removeFromEcosystem -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + + } catch (err) { + + console.log("[ERROR] TeamSetup -> deleteTeam -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + + } + } + +} diff --git a/AABrowserAPI/UserAuthentication.js b/AABrowserAPI/UserAuthentication.js new file mode 100644 index 000000000..dd983412b --- /dev/null +++ b/AABrowserAPI/UserAuthentication.js @@ -0,0 +1,102 @@ +exports.newUserAuthentication = function newUserAuthentication() { + + MAX_STORAGE_PERMISSION_DAYS = 3; + + let thisObject = { + authenticateUser: authenticateUser, + initialize: initialize + }; + + let sessionManager; + let storageAccessManager; + + return thisObject; + + function initialize(pSessionManager, pStorageAccessManager) { + + sessionManager = pSessionManager; + storageAccessManager = pStorageAccessManager; + + } + + function authenticateUser(pSessionToken, callBackFunction) { + + let userProfile = { + userName: "", + devTeams: [], + storagePermissions: [] + }; + + /* The first possibility is that the sessionToken is not there. */ + + if (pSessionToken === "") { + + callBackFunction(global.DEFAULT_OK_RESPONSE, userProfile); + return; + } + + /* The second posiibility is that the session is not found. */ + + let session = sessionManager.getSession(pSessionToken); + + if (session === undefined) { + + let customResponse = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: "Invalid session token received. Please login again." + }; + + callBackFunction(customResponse, userProfile); + return; + + } + + /* The last one is that the session is found, meaning valid. */ + + userProfile.userName = session.userName; + userProfile.devTeams = session.devTeams; + + for (let i = 0; i < session.devTeams.length; i++) { + + let devTeam = session.devTeams[i]; + let container = devTeam.codeName.toLowerCase(); + + let readPermission = storageAccessManager.getPermission(container, "READ", MAX_STORAGE_PERMISSION_DAYS); + let writePermission = storageAccessManager.getPermission(container, "WRITE", MAX_STORAGE_PERMISSION_DAYS); + let deletePermission = storageAccessManager.getPermission(container, "DELETE", MAX_STORAGE_PERMISSION_DAYS); + + userProfile.storagePermissions.push([devTeam.codeName + "." + "READ", readPermission]); + userProfile.storagePermissions.push([devTeam.codeName + "." + "WRITE", writePermission]); + userProfile.storagePermissions.push([devTeam.codeName + "." + "DELETE", deletePermission]); + + console.log(devTeam.codeName + "." + "READ" + ":" + readPermission); + console.log(devTeam.codeName + "." + "WRITE" + ":" + writePermission); + console.log(devTeam.codeName + "." + "DELETE" + ":" + writePermission); + + /* Each devTeam potentially depends on data from other devTeams. The user will need the storage permissions to read that data. */ + + for (j = 0; j < devTeam.devTeamDependencies.length; j++) { + + let dependecy = devTeam.devTeamDependencies[j]; + + let container = dependecy.toLowerCase(); + + let readPermission = storageAccessManager.getPermission(container, "READ", MAX_STORAGE_PERMISSION_DAYS); + + userProfile.storagePermissions.push([dependecy + "." + "READ", readPermission]); + + console.log(dependecy + "." + "READ" + ":" + readPermission); + + } + } + + /* In order to be able to download a bot source code, the user will need READ permissions over the Platform container. */ + + let container = "aaplatform"; + let readPermission = storageAccessManager.getPermission(container, "READ", MAX_STORAGE_PERMISSION_DAYS); + userProfile.storagePermissions.push(["AAPlatform" + "." + "READ", readPermission]); + + callBackFunction(global.DEFAULT_OK_RESPONSE, userProfile); + + } +}; \ No newline at end of file diff --git a/AccumulatedVolumeBar.js b/AccumulatedVolumeBar.js deleted file mode 100644 index 191a44b46..000000000 --- a/AccumulatedVolumeBar.js +++ /dev/null @@ -1,12 +0,0 @@ -function newAccumulatedVolumeBar() { - - var accumulatedVolumeBar = { - begin: undefined, - end: undefined, - accumulatedBuys: 0, - accumulatedSells: 0 - }; - - return accumulatedVolumeBar; - -} \ No newline at end of file diff --git a/Animation.js b/Animation.js deleted file mode 100644 index 377c49395..000000000 --- a/Animation.js +++ /dev/null @@ -1,154 +0,0 @@ - -function newAnimation() { - - const MODULE_NAME = "Animation"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - 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 - }; - - return thisObject; - - function initialize(callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - /* Nothing to do here yet. */ - callBackFunction(GLOBAL.CUSTOM_OK_RESPONSE); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - - function start(callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] start -> Entering function."); } - - animationLoop(); // Inside this function the animation process is started, and at the same time it creates a loop. - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] start -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function stop(callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] stop -> Entering function."); } - - window.cancelAnimationFrame(animationLoopHandle); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] stop -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function addCallBackFunction(key, callBack, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] addCallBackFunction -> Entering function."); } - - callBackFunctions.set(key, callBack); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] addCallBackFunction -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function removeCallBackFunction(key, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] removeCallBackFunction -> Entering function."); } - - callBackFunctions.delete(key); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] removeCallBackFunction -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function animationLoop(callBackFunction) { - - //try { - - if (INFO_LOG === true) { logger.write("[INFO] animationLoop -> Entering function."); } - - /* First thing is to clear the actual canvas */ - - clearBrowserCanvas(); - - /* We loop through the callback functions collections and execute them all. */ - - callBackFunctions.forEach(function (callBackFunction) { - - callBackFunction(); - - }); - - /* We request the next frame to be drawn, and stablishing a loop */ - - animationLoopHandle = window.requestAnimationFrame(animationLoop); - - // } catch (err) { - - // if (ERROR_LOG === true) { logger.write("[ERROR] animationLoop -> err = " + err); } - - // if (callBackFunction !== undefined) { // When the loop is called by the browser there will be no callBackFunction. - // callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - // } - // } - } - - function clearBrowserCanvas() { - - if (INFO_LOG === true) { logger.write("[INFO] clearBrowserCanvas -> Entering function."); } - - browserCanvasContext.clearRect(0, 0, browserCanvas.width, browserCanvas.height); - - } -} - - - - - - - diff --git a/Button.js b/Button.js deleted file mode 100644 index 4a94a1d5f..000000000 --- a/Button.js +++ /dev/null @@ -1,677 +0,0 @@ -function newButton() { - - var button = { - container: undefined, - press: press, - draw: draw, - offFillStyle: 'rgba(150, 150, 250, 1)', - onFillStyle: 'rgba(250, 150, 150, 1)', - status: 'off', - type: undefined, - getContainer: getContainer, // returns the inner most container that holds the point received by parameter. - initialize: initialize - }; - - var container = newContainer(); - container.initialize(); - container.isDraggeable = false; - container.isClickeable = true; - button.container = container; - - const REDUCTION_FACTOR = 3; - - return button; - - function initialize() { - - /* Lets set the basic dimensions of this button. */ - - var position = { - x: 0, - y: 0 - }; - - this.container.frame.position = position; - this.container.frame.width = 50 / REDUCTION_FACTOR; - this.container.frame.height = 50 / REDUCTION_FACTOR; - - /* Now we start listening to the onClick event from the mouse, so that we can allow users operate this button. */ - - this.container.eventHandler.listenToEvent('onMouseClick', press, undefined); - - } - - function getContainer(point) { - - var container; - - /* First we check if this point is inside this space. */ - - if (this.container.frame.isThisPointHere(point, true) === true) { - - return this.container; - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - - } - - function press(event, extraData) { - - button.status = 'on'; - - } - - function draw() { - - switch (this.type) { - - case "Fast Backwards": - - drawFastBackwards(); - break; - - case "Play Backwards": - - drawPlayBackwards(); - break; - - case "Step Backwards": - - drawStepBackwards(); - break; - - case "Pause": - - drawPause(); - break; - - case "Step Forward": - - drawStepForward(); - break; - - case "Play Forward": - - drawPlayForward(); - break; - - case "Fast Forward": - - drawFastForward(); - break; - - default: - - - } - - } - - - function drawFastBackwards() { - - point1 = { - x: 40 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point2 = { - x: 25 / REDUCTION_FACTOR, - y: 25 / REDUCTION_FACTOR - }; - - point3 = { - x: 40 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - - - - - point5 = { - x: 25 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point6 = { - x: 10 / REDUCTION_FACTOR, - y: 25 / REDUCTION_FACTOR - }; - - point7 = { - x: 25 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - - - /* Now the transformations. */ - - point1 = button.container.frame.frameThisPoint(point1); - point2 = button.container.frame.frameThisPoint(point2); - point3 = button.container.frame.frameThisPoint(point3); - - point5 = button.container.frame.frameThisPoint(point5); - point6 = button.container.frame.frameThisPoint(point6); - point7 = button.container.frame.frameThisPoint(point7); - - point1 = button.container.displacement.displaceThisPoint(point1); - point2 = button.container.displacement.displaceThisPoint(point2); - point3 = button.container.displacement.displaceThisPoint(point3); - - point5 = button.container.displacement.displaceThisPoint(point5); - point6 = button.container.displacement.displaceThisPoint(point6); - point7 = button.container.displacement.displaceThisPoint(point7); - - /* 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.moveTo(point5.x, point5.y); - browserCanvasContext.lineTo(point6.x, point6.y); - browserCanvasContext.lineTo(point7.x, point7.y); - browserCanvasContext.lineTo(point5.x, point5.y); - browserCanvasContext.closePath(); - - if (button.status === "on") { - browserCanvasContext.fillStyle = button.onFillStyle; - } else { - browserCanvasContext.fillStyle = button.offFillStyle; - } - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.5)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - } - - function drawPlayBackwards() { - - point1 = { - x: 40 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point2 = { - x: 10 / REDUCTION_FACTOR, - y: 25 / REDUCTION_FACTOR - }; - - point3 = { - x: 40 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - - /* Now the transformations. */ - - point1 = button.container.frame.frameThisPoint(point1); - point2 = button.container.frame.frameThisPoint(point2); - point3 = button.container.frame.frameThisPoint(point3); - - point1 = button.container.displacement.displaceThisPoint(point1); - point2 = button.container.displacement.displaceThisPoint(point2); - point3 = button.container.displacement.displaceThisPoint(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(); - - if (button.status === "on") { - browserCanvasContext.fillStyle = button.onFillStyle; - } else { - browserCanvasContext.fillStyle = button.offFillStyle; - } - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.5)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - } - - function drawStepBackwards() { - - point1 = { - x: 40 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point2 = { - x: 20 / REDUCTION_FACTOR, - y: 25 / REDUCTION_FACTOR - }; - - point3 = { - x: 40 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - - - - - point5 = { - x: 10 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point6 = { - x: 20 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point7 = { - x: 20 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - point8 = { - x: 10 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - /* Now the transformations. */ - - point1 = button.container.frame.frameThisPoint(point1); - point2 = button.container.frame.frameThisPoint(point2); - point3 = button.container.frame.frameThisPoint(point3); - - point5 = button.container.frame.frameThisPoint(point5); - point6 = button.container.frame.frameThisPoint(point6); - point7 = button.container.frame.frameThisPoint(point7); - point8 = button.container.frame.frameThisPoint(point8); - - point1 = button.container.displacement.displaceThisPoint(point1); - point2 = button.container.displacement.displaceThisPoint(point2); - point3 = button.container.displacement.displaceThisPoint(point3); - - point5 = button.container.displacement.displaceThisPoint(point5); - point6 = button.container.displacement.displaceThisPoint(point6); - point7 = button.container.displacement.displaceThisPoint(point7); - point8 = button.container.displacement.displaceThisPoint(point8); - - /* 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.moveTo(point5.x, point5.y); - browserCanvasContext.lineTo(point6.x, point6.y); - browserCanvasContext.lineTo(point7.x, point7.y); - browserCanvasContext.lineTo(point8.x, point8.y); - browserCanvasContext.lineTo(point5.x, point5.y); - browserCanvasContext.closePath(); - - if (button.status === "on") { - browserCanvasContext.fillStyle = button.onFillStyle; - } else { - browserCanvasContext.fillStyle = button.offFillStyle; - } - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.5)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - } - - function drawPause() { - - point1 = { - x: 10 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point2 = { - x: 20 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point3 = { - x: 20 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - point4 = { - x: 10 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - point5 = { - x: 30 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point6 = { - x: 40 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point7 = { - x: 40 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - point8 = { - x: 30 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - /* Now the transformations. */ - - point1 = button.container.frame.frameThisPoint(point1); - point2 = button.container.frame.frameThisPoint(point2); - point3 = button.container.frame.frameThisPoint(point3); - point4 = button.container.frame.frameThisPoint(point4); - point5 = button.container.frame.frameThisPoint(point5); - point6 = button.container.frame.frameThisPoint(point6); - point7 = button.container.frame.frameThisPoint(point7); - point8 = button.container.frame.frameThisPoint(point8); - - point1 = button.container.displacement.displaceThisPoint(point1); - point2 = button.container.displacement.displaceThisPoint(point2); - point3 = button.container.displacement.displaceThisPoint(point3); - point4 = button.container.displacement.displaceThisPoint(point4); - point5 = button.container.displacement.displaceThisPoint(point5); - point6 = button.container.displacement.displaceThisPoint(point6); - point7 = button.container.displacement.displaceThisPoint(point7); - point8 = button.container.displacement.displaceThisPoint(point8); - - /* 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.moveTo(point5.x, point5.y); - browserCanvasContext.lineTo(point6.x, point6.y); - browserCanvasContext.lineTo(point7.x, point7.y); - browserCanvasContext.lineTo(point8.x, point8.y); - browserCanvasContext.lineTo(point5.x, point5.y); - browserCanvasContext.closePath(); - - if (button.status === "on") { - browserCanvasContext.fillStyle = button.onFillStyle; - } else { - browserCanvasContext.fillStyle = button.offFillStyle; - } - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.5)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - } - - function drawStepForward() { - - point1 = { - x: 10 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point2 = { - x: 30 / REDUCTION_FACTOR, - y: 25 / REDUCTION_FACTOR - }; - - point3 = { - x: 10 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - - - - - point5 = { - x: 30 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point6 = { - x: 40 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point7 = { - x: 40 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - point8 = { - x: 30 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - /* Now the transformations. */ - - point1 = button.container.frame.frameThisPoint(point1); - point2 = button.container.frame.frameThisPoint(point2); - point3 = button.container.frame.frameThisPoint(point3); - - point5 = button.container.frame.frameThisPoint(point5); - point6 = button.container.frame.frameThisPoint(point6); - point7 = button.container.frame.frameThisPoint(point7); - point8 = button.container.frame.frameThisPoint(point8); - - point1 = button.container.displacement.displaceThisPoint(point1); - point2 = button.container.displacement.displaceThisPoint(point2); - point3 = button.container.displacement.displaceThisPoint(point3); - - point5 = button.container.displacement.displaceThisPoint(point5); - point6 = button.container.displacement.displaceThisPoint(point6); - point7 = button.container.displacement.displaceThisPoint(point7); - point8 = button.container.displacement.displaceThisPoint(point8); - - /* 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.moveTo(point5.x, point5.y); - browserCanvasContext.lineTo(point6.x, point6.y); - browserCanvasContext.lineTo(point7.x, point7.y); - browserCanvasContext.lineTo(point8.x, point8.y); - browserCanvasContext.lineTo(point5.x, point5.y); - browserCanvasContext.closePath(); - - if (button.status === "on") { - browserCanvasContext.fillStyle = button.onFillStyle; - } else { - browserCanvasContext.fillStyle = button.offFillStyle; - } - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.5)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - } - - function drawPlayForward() { - - point1 = { - x: 10 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point2 = { - x: 40 / REDUCTION_FACTOR, - y: 25 / REDUCTION_FACTOR - }; - - point3 = { - x: 10 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - - /* Now the transformations. */ - - point1 = button.container.frame.frameThisPoint(point1); - point2 = button.container.frame.frameThisPoint(point2); - point3 = button.container.frame.frameThisPoint(point3); - - point1 = button.container.displacement.displaceThisPoint(point1); - point2 = button.container.displacement.displaceThisPoint(point2); - point3 = button.container.displacement.displaceThisPoint(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(); - - if (button.status === "on") { - browserCanvasContext.fillStyle = button.onFillStyle; - } else { - browserCanvasContext.fillStyle = button.offFillStyle; - } - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.5)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - } - - - function drawFastForward() { - - point1 = { - x: 10 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point2 = { - x: 25 / REDUCTION_FACTOR, - y: 25 / REDUCTION_FACTOR - }; - - point3 = { - x: 10 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - - - - - point5 = { - x: 25 / REDUCTION_FACTOR, - y: 10 / REDUCTION_FACTOR - }; - - point6 = { - x: 40 / REDUCTION_FACTOR, - y: 25 / REDUCTION_FACTOR - }; - - point7 = { - x: 25 / REDUCTION_FACTOR, - y: 40 / REDUCTION_FACTOR - }; - - - - /* Now the transformations. */ - - point1 = button.container.frame.frameThisPoint(point1); - point2 = button.container.frame.frameThisPoint(point2); - point3 = button.container.frame.frameThisPoint(point3); - - point5 = button.container.frame.frameThisPoint(point5); - point6 = button.container.frame.frameThisPoint(point6); - point7 = button.container.frame.frameThisPoint(point7); - - point1 = button.container.displacement.displaceThisPoint(point1); - point2 = button.container.displacement.displaceThisPoint(point2); - point3 = button.container.displacement.displaceThisPoint(point3); - - point5 = button.container.displacement.displaceThisPoint(point5); - point6 = button.container.displacement.displaceThisPoint(point6); - point7 = button.container.displacement.displaceThisPoint(point7); - - /* 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.moveTo(point5.x, point5.y); - browserCanvasContext.lineTo(point6.x, point6.y); - browserCanvasContext.lineTo(point7.x, point7.y); - browserCanvasContext.lineTo(point5.x, point5.y); - browserCanvasContext.closePath(); - - if (button.status === "on") { - browserCanvasContext.fillStyle = button.onFillStyle; - } else { - browserCanvasContext.fillStyle = button.offFillStyle; - } - - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.5)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - } - - - - - - - -} \ No newline at end of file diff --git a/Candle.js b/Candle.js deleted file mode 100644 index 40f14ea15..000000000 --- a/Candle.js +++ /dev/null @@ -1,17 +0,0 @@ -function newCandle() { - - var candle = { - open: undefined, - close: undefined, - min: 10000000000000, - max: 0, - begin: undefined, - end: undefined, - direction: undefined, - beginId: undefined, - endId: undefined - }; - - return candle; - -} \ No newline at end of file diff --git a/Canvas.js b/Canvas.js deleted file mode 100644 index e6855f970..000000000 --- a/Canvas.js +++ /dev/null @@ -1,476 +0,0 @@ - -/* - -Whithin this sub-system, the canvas object represents a layer on top of the browser canvas object. - -Graphically, this sub-system has 3 spaces: - -1) The "Chart Space": It is where the charts are plotted. - -2) The "Floating Space": It is where floating elements live. There is a physics engine for this layer that allows these elements to flow. - -3) The "Panels Space": It is where panels live. --> This space has yet to be develop, currently pannels are somehow at the Chart Space. - -All these spaces are child objects of the Canvas object. - -Canvas - | - | - ---> chartSpace - | - | - ---> flaotingSpace - | - | - ---> panelsSpace - -*/ - -let browserCanvas; // This is the canvas object of the browser. -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 INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - /* Mouse event related variables. */ - - let containerDragStarted = false; - let floatingObjectDragStarted = false; - let floatingObjectBeingDragged; - let containerBeingDragged; - let viewPortBeingDragged = false; - - let dragVector = { - downX: 0, - downY: 0, - upX: 0, - upY: 0 - }; - - /* canvas object */ - - let thisObject = { - eventHandler: undefined, - chartSpace: undefined, - floatingSpace: undefined, - panelsSpace: undefined, - animation: undefined, - initialize: initialize - }; - - thisObject.eventHandler = newEventHandler(); - - let splashScreen; - - return thisObject; - - function initialize(callBackFunction) { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - initializeBrowserCanvas(); - - addCanvasEvents(); - - /* Instantiate all the children of Canvas object */ - - let panelsSpace = newPanelsSpace(); - panelsSpace.initialize(); - - this.panelsSpace = panelsSpace; - - let floatingSpace = newFloatingSpace(); - floatingSpace.initialize(); - - this.floatingSpace = floatingSpace; - - let chartSpace = newChartSpace(); - chartSpace.initialize(); - - this.chartSpace = chartSpace; - - /* Splash Screen */ - - splashScreen = newSplashScreen(); - splashScreen.initialize(); - - let animation = newAnimation(); - animation.initialize(onAnimationInitialized); - - function onAnimationInitialized(err) { - - this.animation = animation; - - /* Here we add all the functions that will be called during the animation cycle. */ - - animation.addCallBackFunction("Chart Space", thisObject.chartSpace.draw, onFunctionAdded); - animation.addCallBackFunction("Floating Space", thisObject.floatingSpace.floatingLayer.physicsLoop, onFunctionAdded); - animation.addCallBackFunction("Panels Space", thisObject.panelsSpace.draw, onFunctionAdded); - animation.addCallBackFunction("Splash Screen", splashScreen.draw, onFunctionAdded); - animation.addCallBackFunction("ViewPort Animate", viewPort.animate, onFunctionAdded); - animation.addCallBackFunction("ViewPort Draw", viewPort.draw, onFunctionAdded); - animation.start(onStart); - - function onFunctionAdded(err) { - - if (err.result === GLOBAL.DEFAULT_FAIL_RESPONSE.result) { - - animation.stop(); - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onAnimationInitialized -> onFunctionAdded -> Animation Stopped since a vital funtion could not be added."); } - - /* Display some Error Page here. */ - } - } - - function onStart(err) { - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onAnimationInitialized -> onStart -> Received OK Response."); } - callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE); - return; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onAnimationInitialized -> onStart -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onAnimationInitialized -> onStart -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - } - } - } - - function initializeBrowserCanvas() { - - if (INFO_LOG === true) { logger.write("[INFO] initializeBrowserCanvas -> Entering function."); } - - browserCanvas = document.getElementById('canvas'); - browserCanvasContext = browserCanvas.getContext('2d'); - - let body = document.getElementById('body'); - - browserCanvas.width = window.innerWidth; - browserCanvas.height = window.innerHeight; - browserCanvas.style.border = "none"; - - viewPort.initialize(); - } - - function addCanvasEvents() { - - if (INFO_LOG === true) { logger.write("[INFO] addCanvasEvents -> Entering function."); } - - /* Mouse down and up events to control the drag of the canvas. */ - - browserCanvas.addEventListener('mousedown', onMouseDown, false); - browserCanvas.addEventListener('mouseup', onMouseUp, false); - browserCanvas.addEventListener('mousemove', onMouseMove, false); - browserCanvas.addEventListener('click', onMouseClick, false); - - /* Mouse wheel events. */ - - if (browserCanvas.addEventListener) { - // IE9, Chrome, Safari, Opera - browserCanvas.addEventListener("mousewheel", onMouseWheel, false); - // Firefox - browserCanvas.addEventListener("DOMMouseScroll", onMouseWheel, false); - } - // IE 6/7/8 - else browserCanvas.attachEvent("onmousewheel", onMouseWheel); - - } - - function onMouseDown(event) { - - if (INFO_LOG === true) { logger.write("[INFO] onMouseDown -> Entering function."); } - - /* - - There are four types of elements that can be dragged. - - 1. Panels. - 2. Floating Elements (Currently only FloatingObjects). - 3. Charts. - 4. The Viewport. - - We eveluate each space in order to see if they are holding the element being dragged, and we fallout at the Viewport. - - */ - - dragVector.downX = event.pageX; - dragVector.downY = event.pageY; - - let point = { - x: event.pageX, - y: event.pageY - }; - - let container; - - /* We check first if the mouse is over a panel/ */ - - container = thisObject.panelsSpace.getContainer(point); - - if (container !== undefined && container.isDraggeable === true) { - - containerBeingDragged = container; - containerDragStarted = true; - return; - } - - /* We check first if the mouse is over a floatingObject/ */ - - floatingObjectBeingDragged = thisObject.floatingSpace.floatingLayer.isInside(event.pageX, event.pageY); - - if (floatingObjectBeingDragged >= 0) { - floatingObjectDragStarted = true; - return; - } - - /* If it is not, then we check if it is over any of the existing containers at the Chart Space. */ - - container = thisObject.chartSpace.getContainer(point); - - if (container !== undefined && container.isDraggeable === true) { - - containerBeingDragged = container; - containerDragStarted = true; - return; - } - - viewPortBeingDragged = true; - } - - function onMouseClick(event) { - - if (INFO_LOG === true) { logger.write("[INFO] onMouseClick -> Entering function."); } - - let point = { - x: event.pageX, - y: event.pageY - }; - - let container; - - /* We check first if the mouse is over a panel/ */ - - container = thisObject.panelsSpace.getContainer(point); - - if (container !== undefined && container.isClickeable === true) { - - container.eventHandler.raiseEvent('onMouseClick', event); - return; - } - - /* We check first if the mouse is over a floatingObject/ */ - - let floatingObjectBeingClicked = thisObject.floatingSpace.floatingLayer.isInside(event.pageX, event.pageY); - - if (floatingObjectBeingClicked >= 0) { - - /* Right now we do nothing with this. */ - return; - - } - - /* If it is not, then we check if it is over any of the existing containers at the Chart Space. */ - - container = thisObject.chartSpace.getContainer(point); - - if (container !== undefined && container.isClickeable === true) { - - container.eventHandler.raiseEvent('onMouseClick', event); - return; - } - } - - function onMouseUp(event) { - - if (INFO_LOG === true) { logger.write("[INFO] onMouseUp -> Entering function."); } - - if (containerDragStarted || viewPortBeingDragged || floatingObjectDragStarted) { - - thisObject.eventHandler.raiseEvent("Drag Finished", undefined); - - } - - /* Turn off all the possible things that can be dragged. */ - - containerDragStarted = false; - floatingObjectDragStarted = false; - viewPortBeingDragged = false; - - containerBeingDragged = undefined; - - browserCanvas.style.cursor = "auto"; - - } - - function onMouseMove(event) { - - if (INFO_LOG === true) { logger.write("[INFO] onMouseMove -> Entering function."); } - - viewPort.mousePosition.x = event.pageX; - viewPort.mousePosition.y = event.pageY; - - if (containerDragStarted === true || floatingObjectDragStarted === true || viewPortBeingDragged === true) { - - if (floatingObjectDragStarted === true) { - - if (thisObject.floatingSpace.floatingLayer.isInsideFloatingObject(floatingObjectBeingDragged, event.pageX, event.pageY) === false) { - - /* This means that the user stop moving the mouse and the floatingObject floatingObject out of the pointer. - In this case we cancell the drag operation . */ - - floatingObjectDragStarted = false; - browserCanvas.style.cursor = "auto"; - return; - } - - } - - dragVector.upX = event.pageX; - dragVector.upY = event.pageY; - - checkDrag(); - } - } - - function onMouseWheel(event) { - - if (INFO_LOG === true) { logger.write("[INFO] onMouseWheel -> Entering function."); } - - // cross-browser wheel delta - var event = window.event || event; // old IE support - let delta = Math.max(-1, Math.min(1, event.wheelDelta || -event.detail)); - - /* We try first with panels. */ - - let panelContainer = canvas.panelsSpace.getContainer({ x: event.pageX, y: event.pageY }); - - if (panelContainer !== undefined && panelContainer.isWheelable === true) { - - panelContainer.eventHandler.raiseEvent("Mouse Wheel", event.wheelDelta); - return false; // This instructs the browser not to take the event and scroll the page. - } - - /* We try second with floating objects. */ - - let floatingObjectIndex = canvas.floatingSpace.floatingLayer.isInside(event.pageX, event.pageY); - - if (floatingObjectIndex > 0) { - - canvas.floatingSpace.floatingLayer.changeTargetRepulsion(event.wheelDelta); - - } else { - - viewPort.applyZoom(delta); - - } - - return false; // This instructs the browser not to take the event and scroll the page. - } - - function checkDrag() { - - if (INFO_LOG === true) { logger.write("[INFO] checkDrag -> Entering function."); } - - if (containerDragStarted === true || floatingObjectDragStarted === true || viewPortBeingDragged === true) { - - browserCanvas.style.cursor = "grabbing"; - thisObject.eventHandler.raiseEvent("Dragging", undefined); - - let targetFloatingObject = thisObject.floatingSpace.floatingLayer.isInside(event.pageX, event.pageY); - - if (floatingObjectDragStarted) { - - let floatingObject = thisObject.floatingSpace.floatingLayer.getFloatingObject(undefined, floatingObjectBeingDragged); - - floatingObject.currentPosition.x = dragVector.upX; - floatingObject.currentPosition.y = dragVector.upY; - - } - - if (containerDragStarted || viewPortBeingDragged) { - - /* The parameters received have been captured with zoom applied. We must remove the zoom in order to correctly modify the displacement. */ - - - let downCopy = { - x: dragVector.downX, - y: dragVector.downY - }; - - let downCopyNoTransf; - downCopyNoTransf = viewPort.unzoomThisPoint(downCopy); - //downCopyNoTransf = containerBeingDragged.zoom.unzoomThisPoint(downCopyNoTransf); - - let upCopy = { - x: dragVector.upX, - y: dragVector.upY - }; - - let upCopyNoTranf; - upCopyNoTranf = viewPort.unzoomThisPoint(upCopy); - //upCopyNoTranf = containerBeingDragged.zoom.unzoomThisPoint(upCopyNoTranf); - - /* - let displaceVector = { - x: upCopyNoTranf.x - downCopyNoTransf.x, - y: upCopyNoTranf.y - downCopyNoTransf.y - }; - */ - - let displaceVector = { - x: dragVector.upX - dragVector.downX, - y: dragVector.upY - dragVector.downY - }; - - - if (viewPortBeingDragged) { - - viewPort.displace(displaceVector); - - } - - - if (containerBeingDragged !== undefined) { - - containerBeingDragged.frame.position.x = containerBeingDragged.frame.position.x + displaceVector.x; - containerBeingDragged.frame.position.y = containerBeingDragged.frame.position.y + displaceVector.y; - - } - } - - /* Finally we set the starting point of the new dragVector at this current point. */ - - dragVector.downX = dragVector.upX; - dragVector.downY = dragVector.upY; - - } - - } - -} - - diff --git a/ChartGrid.js b/ChartGrid.js deleted file mode 100644 index 42323b289..000000000 --- a/ChartGrid.js +++ /dev/null @@ -1,678 +0,0 @@ -function newChartGrid() { - - - - var chartGrid = { - draw: draw - }; - - return chartGrid; - - - function draw(container, timeLineCoordinateSystem) { - - let periods = []; - let boxes = []; - let labels = []; - - const basePeriod = { - x: 8 * 60 * 60 * 1000, - y: timeLineCoordinateSystem.max.y / 1000 / CHART_ASPECT_RATIO - }; - - let period = { - x: basePeriod.x, - y: basePeriod.y - }; - - calculateTimeLabels(); - printLabels(1000, 10000); - - function calculateTimeLabels() { - - periods.push(period); // 0 - - period = { - x: basePeriod.x / 8, - y: basePeriod.y / 10 - }; - - periods.push(period); // 1 - - period = { - x: basePeriod.x / 8 / 6, - y: basePeriod.y / 100 - }; - - periods.push(period); // 2 - - period = { - x: basePeriod.x * 3, - y: basePeriod.y * 10 - }; - - periods.push(period); // 3 - - period = { - x: basePeriod.x * 3 * 7, - y: basePeriod.y * 100 - }; - - periods.push(period); // 4 - - /* Period #2 Plotting */ - - boxes = []; - plotGrid(periods[2]); - labels = []; - - /* Label Calculation */ - - for (let i = 0; i < boxes.length; i++) { - - const UPPER_PERIOD = 1; - - let box = boxes[i]; - let currentValue = box.topLeft; - - currentValue = unTransformThisPoint(currentValue, container); - currentValue = timeLineCoordinateSystem.unInverseTransform(currentValue, container.frame.height); - - /* We must compare the current value on the current period, against the next period, to know which cell we are in. */ - - let distanceToNextPeriod = Math.trunc((currentValue.x / periods[UPPER_PERIOD].x - Math.trunc(currentValue.x / periods[UPPER_PERIOD].x)) * 1000); - - switch (distanceToNextPeriod) { - case 0: { - labels.push("0"); - break; - } - case Math.trunc(1 / 6 * 1000): { - - } - labels.push("10"); - break; - case Math.trunc(2 / 6 * 1000): { - - } - labels.push("20"); - break; - - case Math.trunc(3 / 6 * 1000): { - - } - labels.push("30"); - break; - - case Math.trunc(4 / 6 * 1000): { - - } - labels.push("40"); - break; - - case Math.trunc(5 / 6 * 1000): { - - } - labels.push("50"); - break; - - } - - } - - printLabels(1, 150); - - /* Period #1 Plotting */ - - boxes = []; - plotGrid(periods[1]); - labels = []; - - /* Label Calculation */ - - for (let i = 0; i < boxes.length; i++) { - - const UPPER_PERIOD = 3; - - let box = boxes[i]; - let currentValue = box.topLeft; - - currentValue = unTransformThisPoint(currentValue, container); - currentValue = timeLineCoordinateSystem.unInverseTransform(currentValue, container.frame.height); - - /* We must compare the current value on the current period, against the next period, to know which cell we are in. */ - - let distanceToNextPeriod = Math.trunc((currentValue.x / periods[UPPER_PERIOD].x - Math.trunc(currentValue.x / periods[UPPER_PERIOD].x)) * 1000); - - switch (distanceToNextPeriod) { - case 0: { - labels.push("0 Hs"); - break; - } - case Math.trunc(1 / 24 * 1000): { - - } - labels.push("1 Hs"); - break; - case Math.trunc(2 / 24 * 1000): { - - } - labels.push("2 Hs"); - break; - - case Math.trunc(3 / 24 * 1000): { - - } - labels.push("3 Hs"); - break; - - case Math.trunc(4 / 24 * 1000): { - - } - labels.push("4 Hs"); - break; - - case Math.trunc(5 / 24 * 1000): { - - } - labels.push("5 Hs"); - break; - - case Math.trunc(6 / 24 * 1000): { - - } - labels.push("6 Hs"); - break; - - case Math.trunc(7 / 24 * 1000): { - - } - labels.push("7 Hs"); - break; - - case Math.trunc(8 / 24 * 1000): { - - } - labels.push("8 Hs"); - break; - - case Math.trunc(9 / 24 * 1000): { - - } - labels.push("9 Hs"); - break; - - case Math.trunc(10 / 24 * 1000): { - - } - labels.push("10 Hs"); - break; - - case Math.trunc(11 / 24 * 1000): { - - } - labels.push("11 Hs"); - break; - - case Math.trunc(12 / 24 * 1000): { - - } - labels.push("12 Hs"); - break; - - case Math.trunc(13 / 24 * 1000): { - - } - labels.push("13 Hs"); - break; - - case Math.trunc(14 / 24 * 1000): { - - } - labels.push("14 Hs"); - break; - - case Math.trunc(15 / 24 * 1000): { - - } - labels.push("15 Hs"); - break; - - case Math.trunc(16 / 24 * 1000): { - - } - labels.push("16 Hs"); - break; - - case Math.trunc(17 / 24 * 1000): { - - } - labels.push("17 Hs"); - break; - - case Math.trunc(18 / 24 * 1000): { - - } - labels.push("18 Hs"); - break; - - case Math.trunc(19 / 24 * 1000): { - - } - labels.push("19 Hs"); - break; - - case Math.trunc(20 / 24 * 1000): { - - } - labels.push("20 Hs"); - break; - - case Math.trunc(21 / 24 * 1000): { - - } - labels.push("21 Hs"); - break; - - case Math.trunc(22 / 24 * 1000): { - - } - labels.push("22 Hs"); - break; - - case Math.trunc(23 / 24 * 1000): { - - } - labels.push("23 Hs"); - break; - } - - } - - printLabels(5, 250); - - /* Period #0 Plotting */ - - boxes = []; - plotGrid(periods[0]); - labels = []; - - /* Label Calculation */ - - for (let i = 0; i < boxes.length; i++) { - - const UPPER_PERIOD = 3; - - let box = boxes[i]; - let currentValue = box.topLeft; - - currentValue = unTransformThisPoint(currentValue, container); - currentValue = timeLineCoordinateSystem.unInverseTransform(currentValue, container.frame.height); - - /* We must compare the current value on the current period, against the next period, to know which cell we are in. */ - - let distanceToNextPeriod = Math.trunc((currentValue.x / periods[UPPER_PERIOD].x - Math.trunc(currentValue.x / periods[UPPER_PERIOD].x)) * 1000); - - switch (distanceToNextPeriod) { - case 0: { - labels.push("Morning"); - break; - } - case Math.trunc(1 / 3 * 1000): { - - } - labels.push("Afternoon"); - break; - case Math.trunc(2 / 3 * 1000): { - - } - labels.push("Evening"); - break; - } - - } - - printLabels(15, 50); - - /* Period #3 Plotting */ - - boxes = []; - plotGrid(periods[3]); - labels = []; - - /* Label Calculation */ - - for (let i = 0; i < boxes.length; i++) { - - const UPPER_PERIOD = 1; - - let box = boxes[i]; - let currentValue = box.topLeft; - - currentValue = unTransformThisPoint(currentValue, container); - currentValue = timeLineCoordinateSystem.unInverseTransform(currentValue, container.frame.height); - - let date = new Date(currentValue.x); - - labels.push(date.toDateString()); - - } - - printLabels(100, 2250); - - /* Period #4 Plotting */ - - boxes = []; - plotGrid(periods[4]); - labels = []; - - /* Label Calculation */ - - for (let i = 0; i < boxes.length; i++) { - - const UPPER_PERIOD = 1; - - let box = boxes[i]; - let currentValue = box.topLeft; - - currentValue = unTransformThisPoint(currentValue, container); - currentValue = timeLineCoordinateSystem.unInverseTransform(currentValue, container.frame.height); - - let date = new Date(currentValue.x); - - var monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]; - - labels.push(monthNames[date.getMonth()] + ' ' + date.getFullYear()); - - } - - } - - function plotGrid(period) { - - - let periodWidth = period.x; - let periodHeight = period.y; - - let visiblePoint1 = { - x: viewPort.visibleArea.topLeft.x, - y: viewPort.visibleArea.bottomLeft.y - }; - - visiblePoint1 = unTransformThisPoint(visiblePoint1, container); - visiblePoint1 = timeLineCoordinateSystem.unInverseTransform(visiblePoint1, container.frame.height); - - let visiblePoint2 = { - x: viewPort.visibleArea.bottomRight.x, - y: viewPort.visibleArea.topRight.y - }; - - visiblePoint2 = unTransformThisPoint(visiblePoint2, container); - visiblePoint2 = timeLineCoordinateSystem.unInverseTransform(visiblePoint2, container.frame.height); - - startingPoint = { - x: Math.trunc(visiblePoint1.x / periodWidth) * periodWidth, - y: Math.round(Math.trunc(visiblePoint1.y / periodHeight) * periodHeight * 100000) / 100000 - }; - - endingPoint = { - x: Math.trunc(visiblePoint2.x / periodWidth) * periodWidth, - y: Math.trunc(visiblePoint2.y / periodHeight) * periodHeight - }; - - let lineWidth; - - if (viewPort.zoomLevel > 0) { - - lineWidth = period.x / basePeriod.x + period.x / basePeriod.x * viewPort.zoomLevel / 20; - - } else { - - lineWidth = period.x / basePeriod.x + period.x / basePeriod.x * viewPort.zoomLevel / 80; - - } - - - if (lineWidth < 0.7) { - return; - } - - if (lineWidth > 1.4) { - lineWidth = 1.4; - } - - browserCanvasContext.beginPath(); - - let y1; - let y2; - - let lastRightLabelPosition = { - x: 0, - y: 0 - }; - - let lastLeftLabelPosition = { - x: 0, - y: 0 - }; - - for (var j = startingPoint.y; j <= endingPoint.y + periodHeight; j = j + periodHeight) { - - if (j >= 0 && j <= timeLineCoordinateSystem.max.y) { - - let point1 = { - x: visiblePoint1.x, - y: j - }; - - let point2 = { - x: visiblePoint2.x, - y: j - }; - - point1 = timeLineCoordinateSystem.transformThisPoint(point1); - point2 = timeLineCoordinateSystem.transformThisPoint(point2); - - point1 = transformThisPoint(point1, container); - point2 = transformThisPoint(point2, container); - - if (point1.y >= viewPort.visibleArea.topLeft.y) { - - /* Here we keep the last two positive valued of y */ - - y2 = y1; - y1 = point1.y; - - } - - point1 = viewPort.fitIntoVisibleArea(point1); - point2 = viewPort.fitIntoVisibleArea(point2); - - browserCanvasContext.moveTo(point1.x, point1.y); - browserCanvasContext.lineTo(point2.x, point2.y); - - /* Now we put the scale on the right side of the chart */ - - rightScale(); - leftScale(); - - function rightScale() { - - let fontSize = 10; - - let label = ' ' + Math.round(j * 1000000) / 1000000 / 4; // TODO This 4 is to be removed. - - let labelPoint = { - x: viewPort.visibleArea.bottomRight.x + 1, - y: point1.y + fontSize / 2 * FONT_ASPECT_RATIO - }; - - if (Math.abs(lastRightLabelPosition.y - labelPoint.y > fontSize || lastRightLabelPosition.y === 0)) { - - if (labelPoint.y > viewPort.visibleArea.topLeft.y + fontSize && labelPoint.y < viewPort.visibleArea.bottomRight.y - fontSize) { - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - } - } - - lastRightLabelPosition.x = labelPoint.x; - lastRightLabelPosition.y = labelPoint.y; - - } - - function leftScale() { - - let fontSize = 10; - - /* - j is a value between 0 and the max y on the scale of the products. - If we want to convert j to the scale of profits we need to apply the following formula: - */ - - let labelValue = (j - timeLineCoordinateSystem.max.y / 2) * 200 / timeLineCoordinateSystem.max.y; - labelValue = Math.round(labelValue * 1000000) / 1000000; - if (labelValue > 0) { labelValue = '+' + labelValue; } - if (parseInt(labelValue) > -10 && parseInt(labelValue) < 10) { labelValue = ' ' + labelValue; } - if (parseInt(labelValue) === 0) { labelValue = ' ' + labelValue; } - let label = ' ' + pad(labelValue, 3) + '%'; - - let labelPoint = { - x: -2, - y: point1.y + fontSize / 2 * FONT_ASPECT_RATIO - }; - - if (Math.abs(lastLeftLabelPosition.y - labelPoint.y > fontSize || lastLeftLabelPosition.y === 0)) { - - if (labelPoint.y > viewPort.visibleArea.topLeft.y + fontSize && labelPoint.y < viewPort.visibleArea.bottomRight.y - fontSize) { - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - } - } - - lastLeftLabelPosition.x = labelPoint.x; - lastLeftLabelPosition.y = labelPoint.y; - - } - } - } - - let x1; - let x2; - - for (var i = startingPoint.x; i <= endingPoint.x + periodWidth; i = i + periodWidth) { - - let point3 = { - x: i, - y: 0 - }; - - let point4 = { - x: i, - y: timeLineCoordinateSystem.max.y - }; - - point3 = timeLineCoordinateSystem.transformThisPoint(point3); - point4 = timeLineCoordinateSystem.transformThisPoint(point4); - - point3 = transformThisPoint(point3, container); - point4 = transformThisPoint(point4, container); - - /* Here we keep the last two positive valued of y */ - - x2 = x1; - x1 = point3.x; - - if (x2 !== undefined) { - - let topLeft = { - x: x2, - y: y1 - }; - - let bottomRight = { - x: x1, - y: y2 - }; - - let box = { - topLeft: topLeft, - bottomRight: bottomRight - }; - boxes.push(box); - } - - - /* Painting the lines */ - - point3 = viewPort.fitIntoVisibleArea(point3); - point4 = viewPort.fitIntoVisibleArea(point4); - - browserCanvasContext.moveTo(point3.x, point3.y); - browserCanvasContext.lineTo(point4.x, point4.y); - - - - } - - browserCanvasContext.closePath(); - browserCanvasContext.strokeStyle = 'rgba(0, 37, 224, 0.' + Math.trunc(lineWidth + 1) + ')'; - - browserCanvasContext.lineWidth = lineWidth; - - browserCanvasContext.stroke(); - - } - - function printLabels(baseZoomFilter, maxZoom) { - - let zoomFilter = viewPort.zoomFontSize(baseZoomFilter); - - if (zoomFilter < 7 || zoomFilter > maxZoom) { - return; - } - - for (let i = 0; i < boxes.length; i++) { - - let label = labels[i]; - - if (label === undefined) { - label = "undefined"; - } - - let fontSize = 10; - - let labelPoint = { - x: boxes[i].topLeft.x - label.length / 2 * fontSize * FONT_ASPECT_RATIO, - y: viewPort.visibleArea.topLeft.y - fontSize / 2 + 1 - }; - - let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; - - browserCanvasContext.beginPath(); - - browserCanvasContext.rect(labelPoint.x, labelPoint.y - fontSize, xOffset * 2, fontSize ); - browserCanvasContext.fillStyle = 'rgba(255, 255, 255, 1)'; - browserCanvasContext.fill(); - - browserCanvasContext.closePath(); - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - } - - browserCanvasContext.fillStyle = 'rgba(255, 255, 255, 1)'; - browserCanvasContext.beginPath(); - browserCanvasContext.rect(0, 0, viewPort.visibleArea.topLeft.x, viewPort.visibleArea.topLeft.y); - browserCanvasContext.rect(viewPort.visibleArea.topRight.x, 0, 500, viewPort.visibleArea.topLeft.y); - browserCanvasContext.closePath(); - browserCanvasContext.fill(); - } - } -} diff --git a/ChartUtilities.js b/ChartUtilities.js deleted file mode 100644 index 3f0b9ce02..000000000 --- a/ChartUtilities.js +++ /dev/null @@ -1,150 +0,0 @@ - - -function getSideDays(timePeriod) { - - let daysOnSides; - - if (timePeriod < _1_HOUR_IN_MILISECONDS) { - daysOnSides = 1; - } - - if (timePeriod === _1_HOUR_IN_MILISECONDS) { - daysOnSides = 9; - } - - if (timePeriod === _2_HOURS_IN_MILISECONDS) { - daysOnSides = 10; - } - - if (timePeriod === _3_HOURS_IN_MILISECONDS) { - daysOnSides = 12; - } - - if (timePeriod === _4_HOURS_IN_MILISECONDS) { - daysOnSides = 20; - } - - if (timePeriod === _6_HOURS_IN_MILISECONDS) { - daysOnSides = 35; - } - - if (timePeriod === _8_HOURS_IN_MILISECONDS) { - daysOnSides = 44; - } - - if (timePeriod === _12_HOURS_IN_MILISECONDS) { - daysOnSides = 56; - } - - if (timePeriod === ONE_DAY_IN_MILISECONDS) { - daysOnSides = 154; - } - - return daysOnSides; -} - - -function getCondenseFactor(timePeriod) { - - switch (timePeriod) { - 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(timePeriod) { - - switch (timePeriod) { - 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(" recalculatePeriod > " + zoomLevel); - - if (zoomLevel > 200) { - return _1_MINUTE_IN_MILISECONDS; - } - - if (zoomLevel > 120) { - return _2_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > 80) { - return _3_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > 60) { - return _4_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > 15) { - return _5_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > - 7) { - return _10_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > - 10) { - return _15_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > - 13) { - return _20_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > - 15) { - return _30_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > - 16) { - return _40_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > - 17) { - return _45_MINUTES_IN_MILISECONDS; - } - - if (zoomLevel > - 18) { - return _1_HOUR_IN_MILISECONDS; - } - - if (zoomLevel > - 24) { - return _2_HOURS_IN_MILISECONDS; - } - - if (zoomLevel > - 25) { - return _3_HOURS_IN_MILISECONDS; - } - - if (zoomLevel > - 27) { - return _4_HOURS_IN_MILISECONDS; - } - - if (zoomLevel > - 27.5) { - return _6_HOURS_IN_MILISECONDS; - } - - if (zoomLevel > - 27.75) { - return _8_HOURS_IN_MILISECONDS; - } - - if (zoomLevel > - 28) { - return _12_HOURS_IN_MILISECONDS; - } - - if (zoomLevel <= - 28) { - return ONE_DAY_IN_MILISECONDS; - } -} diff --git a/CompetitionStorage.js b/CompetitionStorage.js deleted file mode 100644 index 4ccc18017..000000000 --- a/CompetitionStorage.js +++ /dev/null @@ -1,156 +0,0 @@ - -function newCompetitionStorage(pName) { - - const MODULE_NAME = "Competition Storage"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let thisObject = { - - competitorsSequences: [], - eventHandler: undefined, - initialize: initialize - - } - - thisObject.eventHandler = newEventHandler(); - - /* We name the event Handler to easy debugging. */ - - thisObject.eventHandler.name = "Storage-" + pName; - - let fileSequences = []; - - return thisObject; - - function initialize(pHost, pCompetition, pExchange, pMarket, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> key = " + pHost.codeName + "-" + pCompetition.codeName); } - - let dataSetsToLoad = 0; - let dataSetsLoaded = 0; - - fileSequences = []; - - for (let j = 0; j < pCompetition.participants.length; j++) { - - let devTeam = ecosystem.getTeam(pCompetition.participants[j].devTeam); - let bot = ecosystem.getBot(devTeam, pCompetition.participants[j].bot) - let product = ecosystem.getProduct(bot, "Competition Trading History"); - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> key = " + devTeam.codeName + "-" + bot.codeName + "-" + product.codeName); } - - for (let i = 0; i < product.dataSets.length; i++) { - - let thisSet = product.dataSets[i]; - - switch (thisSet.type) { - case 'File Sequence': { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> File Sequence -> key = " + pHost.codeName + "-" + pCompetition.codeName + "-" + devTeam.codeName + "-" + bot.codeName + "-" + product.codeName); } - - let fileSequence = newFileSequence(); - fileSequence.initialize(devTeam, bot, product, thisSet, pExchange, pMarket, onFileSequenceReady); - fileSequences.push(fileSequence); - dataSetsToLoad++; - } - break; - } - - function onFileSequenceReady(err, pCaller) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> key = " + pHost.codeName + "-" + pCompetition.codeName + "-" + devTeam.codeName + "-" + bot.codeName + "-" + product.codeName); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> err.message = " + err.message); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> This bot has not starting competing yet."); } - - break; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - let event = { - totalValue: pCaller.getExpectedFiles(), - currentValue: pCaller.getFilesLoaded() - } - - thisObject.eventHandler.raiseEvent('File Sequence Loaded', event); - - if (event.currentValue === event.totalValue) { - - dataSetsLoaded++; - - checkInitializeComplete(); - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onFileSequenceReady -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function checkInitializeComplete() { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] checkInitializeComplete -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] checkInitializeComplete -> key = " + pHost.codeName + "-" + pCompetition.codeName + "-" + devTeam.codeName + "-" + bot.codeName + "-" + product.codeName); } - - if (dataSetsLoaded === dataSetsToLoad) { - - callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE); - - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> checkInitializeComplete -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - } - } - - thisObject.competitorsSequences.push(fileSequences); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - -} - diff --git a/Container.js b/Container.js deleted file mode 100644 index c35dfa52c..000000000 --- a/Container.js +++ /dev/null @@ -1,36 +0,0 @@ - - -function newContainer() { - - var container = { - frame: undefined, - displacement: undefined, - eventHandler: undefined, - parentContainer: undefined, - isDraggeable: true, - isClickeable: false, - isWheelable: false, - name: undefined, - initialize: initialize - }; - - return container; - - function initialize() { - - var frame = newFrame(); - frame.initialize(); - this.frame = frame; - frame.container = container; - - var eventHandler = newEventHandler(); - eventHandler.initialize(); - eventHandler.name = container.name; - this.eventHandler = eventHandler; - - var displacement = newDisplacement(); - this.displacement = displacement; - displacement.container = container; - - } -} \ No newline at end of file diff --git a/Dashboard.js b/Dashboard.js deleted file mode 100644 index 3f2ae9899..000000000 --- a/Dashboard.js +++ /dev/null @@ -1,195 +0,0 @@ -/* Callbacks default responses. */ - -let GLOBAL = {}; - -GLOBAL.DEFAULT_OK_RESPONSE = { - result: "Ok", - message: "Operation Succeeded" -}; - -GLOBAL.DEFAULT_FAIL_RESPONSE = { - result: "Fail", - message: "Operation Failed" -}; - -GLOBAL.DEFAULT_RETRY_RESPONSE = { - result: "Retry", - message: "Retry Later" -}; - -GLOBAL.CUSTOM_OK_RESPONSE = { - result: "Ok, but check Message", - message: "Custom Message" -}; - -GLOBAL.CUSTOM_FAIL_RESPONSE = { - result: "Fail Because", - message: "Custom Message" -}; - -/* The Dashboard is the root object that contains all the other stuff in it. Some of that stuff is visible, some other is invisible. */ - -let canvas; -let markets; -let ecosystem = newEcosystem(); - - -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); - -function convertTimePeriodToName(pTimePeriod) { - - for (let i = 0; i < dailyFilePeriods.length; i++) { - - let period = dailyFilePeriods[i]; - - if (period[0] === pTimePeriod) { - - return period[1]; - - } - } - - for (let i = 0; i < marketFilesPeriods.length; i++) { - - let period = marketFilesPeriods[i]; - - if (period[0] === pTimePeriod) { - - return period[1]; - - } - } -} - - - -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. -const INITIAL_DEFAULT_MARKET = 2; // This is the market that will be shown when loading the site for the first time. -const DEFAULT_EXCHANGE = "Poloniex"; -const DEFAULT_MARKET = { - assetA: "USDT", - assetB: "BTC", -}; - -const USDT_BTC_HTH = 19900; // 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; - -const EARLIEST_DATE = new Date(2014, 0, 18, 4, 26, 8); -const FONT_ASPECT_RATIO = 0.60; -const CHART_ASPECT_RATIO = 1 / 10; - -const INITIAL_ZOOM_LEVEL = -22; // This is the zoom level at the view port in which the APP starts. -const INITIAL_TIME_PERIOD = recalculatePeriod(INITIAL_ZOOM_LEVEL); - -var viewPort = newViewPort(); - - - -const PRODUCT_CARD_STATUS = { - ON: 'on', - LOADING: 'loading', - OFF: 'off' -}; - -/* 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"; - -var newDate = new Date(); -newDate.setMilliseconds(0); -newDate.setDate(newDate.getDate() - 90); - -const INITIAL_DATE = new Date(); - -var maxDate = new Date(); -maxDate.setMilliseconds(0); -maxDate.setDate(maxDate.getDate() + 10); - -const MAX_PLOTABLE_DATE = maxDate; - -function dashboardStart() { - - setTimeout(start, DEBUG_START_UP_DELAY); - -} - - -function start() { - - const ID_EXCHANGE_POLONIEX = 1; - - /* For now, we are supporting only one market. */ - - let market = { - id: 2, - assetA: "USDT", - assetB: "BTC" - }; - - markets = new Map(); - - markets.set(market.id, market); - - canvas = newCanvas(); - canvas.initialize(); - -} - diff --git a/DebugLog.js b/DebugLog.js deleted file mode 100644 index 34ef99a0c..000000000 --- a/DebugLog.js +++ /dev/null @@ -1,16 +0,0 @@ - -function newDebugLog() { - - let thisObject = { - write: write, - fileName: undefined - } - - return thisObject; - - function write(pText) { - - console.log(thisObject.fileName + " : " + pText); - - } -} \ No newline at end of file diff --git a/Displace.js b/Displace.js deleted file mode 100644 index 2a4d8ca1c..000000000 --- a/Displace.js +++ /dev/null @@ -1,81 +0,0 @@ - -/* - -We call displacement to the offset we can produce to the object which the displcement object is is a child from. -The displacement is a vector that displaces everything static drawn on on the context of the parent object. - -*/ - -function newDisplacement() { - - var displacement = { - containerName: "", // This is for debugging purposes only. - parentDisplacement: undefined, // Here we store the displacement object of the parent cointainer, if exists. - container: undefined, - x: 0, // This is the actual x displacement. - y: 0, // This is the actual y displacement. - displace: displace, - undisplaceThisPoint: undisplaceThisPoint, - displaceThisPoint: displaceThisPoint - }; - - return displacement; - - - function displace(amountX, amountY) { - - /* First we check if by allowing this displacement we would produce the current frame to be beyond its limit. */ - - tempDisplacement = newDisplacement(); - tempDisplacement.x = amountX; - tempDisplacement.y = amountY; - - if (this.container.frame.canYouMoveHere(tempDisplacement)) { - - /* Now we displace */ - - displacement.x = displacement.x + amountX; - displacement.y = displacement.y + amountY; - - return true; - } - - return false; - } - - function displaceThisPoint(point) { - - if (this.parentDisplacement !== undefined) { - - point = this.parentDisplacement.displaceThisPoint(point); - - } - - point.x = point.x + displacement.x; - point.y = point.y + displacement.y; - - return point; - } - - function undisplaceThisPoint(point) { - - if (this.parentDisplacement !== undefined) { - - point = this.parentDisplacement.undisplaceThisPoint(point); - - } - - point.x = point.x - displacement.x; - point.y = point.y - displacement.y; - - return point; - } - -} - - - - - - - diff --git a/Ecosystem.js b/Ecosystem.js index 11a633278..3685fd45a 100644 --- a/Ecosystem.js +++ b/Ecosystem.js @@ -1,7 +1,7 @@  function newEcosystem() { - let ecosystem = { + let thisObject = { getHost: getHost, getCompetition: getCompetition, getTeam: getTeam, @@ -12,17 +12,23 @@ function newEcosystem() { getExchange: getExchange, getPlotter: getPlotter, getPlotterModule: getPlotterModule, + getStoragePermissions: getStoragePermissions, initialize: initialize } const ECOSYSTEM = "@ecosystem.json@"; - return ecosystem; - + return thisObject; function initialize() { + } + + function getStoragePermissions() { + + return ECOSYSTEM.STORAGE_PERMISSIONS; + } function getHost(pHostCodeName) { diff --git a/EventHandler.js b/EventHandler.js deleted file mode 100644 index 38907c237..000000000 --- a/EventHandler.js +++ /dev/null @@ -1,63 +0,0 @@ - -function newEventHandler() { - - const CONSOLE_LOG = false; - - let thisObject = { - name: undefined, // This is for debugging purposes only. - parentEventHandler: undefined, // Here we store the parent cointainer zoom object. - listenToEvent: listenToEvent, - raiseEvent: raiseEvent, - initialize: initialize - }; - - var eventHandlers = []; // Here we store all the functions we will call when an event is comming. - - return thisObject; - - function initialize() { - - - } - - function listenToEvent(eventType, handler, extraData) { - - eventHandlers.push([eventType, handler, extraData]); - - if (thisObject.name !== undefined && CONSOLE_LOG === true) { - - console.log("Event Handler named " + thisObject.name + " received a request to listenToEvent " + eventType + " and call " + (handler.toString()).substring(0, 50)); - - } - - } - - function raiseEvent(eventType, event) { - - if (thisObject.name !== undefined && CONSOLE_LOG === true) { - - console.log("Event Handler named " + thisObject.name + " received a request to raiseEvent " + eventType + " with this data " + (event.toString()).substring(0, 50)); - - } - - for (var i = 0; i < eventHandlers.length; i++) { - - /* We will execute all the functions listening to this event type. */ - - if (eventHandlers[i][0] === eventType) { - - var handler = eventHandlers[i][1]; - var extraData = eventHandlers[i][2]; - - if (thisObject.name !== undefined && CONSOLE_LOG === true) { - - console.log("Event Handler named " + thisObject.name + " received a request to raiseEvent " + eventType + " and will call this handler: " + (handler.toString()).substring(0,50)); - - } - - handler(event, extraData); - - } - } - } -} diff --git a/Files/DailyFiles.js b/Files/DailyFiles.js deleted file mode 100644 index bd044b890..000000000 --- a/Files/DailyFiles.js +++ /dev/null @@ -1,282 +0,0 @@ - -function newDailyFiles() { - - const MODULE_NAME = "Daily Files"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let thisObject = { - getFileCursor: getFileCursor, - setDatetime: setDatetime, - setTimePeriod: setTimePeriod, - getExpectedFiles: getExpectedFiles, - getFilesLoaded: getFilesLoaded, - initialize: initialize - } - - let filesLoaded = 0; - let expectedFiles = 0; - - let fileCloud; - - let marketFiles = new Map; - let fileCursors = new Map; - - let callBackWhenFileReceived; - - return thisObject; - - function initialize(pDevTeam, pBot, pProduct, pSet, pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - callBackWhenFileReceived = callBackFunction; - - let exchange = ecosystem.getExchange(pProduct, pExchange); - - if (exchange === undefined) { - - throw "Exchange not supoorted by this product of the ecosystem! - pDevTeam.codeName = " + pDevTeam.codeName + ", pBot.codeName = " + pBot.codeName + ", pProduct.codeName = " + pProduct.codeName + ", pExchange = " + pExchange; - - } - - fileCloud = newFileCloud(); - fileCloud.initialize(pBot); - - /* First we will get the Data Range */ - - fileCloud.getFile(pDevTeam, pBot, pSet, exchange, pMarket, undefined, undefined, undefined, true, onDataRangeReceived); - - function onDataRangeReceived(err, pFile) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Entering function."); } - - let beginDateRange; - let endDateRange; - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received OK Response."); } - - beginDateRange = new Date(pFile.begin); - endDateRange = new Date(pFile.end); - - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> err.message = " + err.message); } - - if (err.message === "File does not exist.") { - - if (INFO_LOG === true) { logger.write("[WARN] initialize -> onFileReceived -> No Date Range file found. maxDate will be set to current datetime. "); } - - endDateRange = new Date(); - break; - } - - if (err.message === "Missing Configuration.") { - - if (ERROR_LOG === true) { logger.write("[WARN] initialize -> onFileReceived -> The needed configuration for the dateRange at the dataSet of the product was not found."); } - if (ERROR_LOG === true) { logger.write("[WARN] initialize -> onFileReceived -> maxDate will be set to current datetime."); } - - endDateRange = new Date(); - break; - } - - /* If none of the previous conditions are met, the we return the err to the caller. */ - - callBackFunction(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - /* Now we will get the daily files */ - - for (i = 0; i < dailyFilePeriods.length; i++) { - - let periodTime = dailyFilePeriods[i][0]; - let periodName = dailyFilePeriods[i][1]; - - if (pSet.validPeriods.includes(periodName) === true) { - - let fileCursor = newFileCursor(); - fileCursor.initialize(fileCloud, pDevTeam, pBot, pSet, exchange, pMarket, periodName, periodTime, pDatetime, pTimePeriod, beginDateRange, endDateRange, onFileReceived); - - fileCursors.set(periodTime, fileCursor); - - expectedFiles = expectedFiles + fileCursor.getExpectedFiles(); - - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onFileReceived -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function onFileReceived(err) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] onFileReceived -> Entering function."); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] onFileReceived -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] onFileReceived -> Received FAIL Response."); } - callBackWhenFileReceived(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] onFileReceived -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] onFileReceived -> err.message = " + err.message); } - - callBackWhenFileReceived(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] onFileReceived -> Received Unexpected Response."); } - callBackWhenFileReceived(err); - return; - } - } - - filesLoaded++; - callBackWhenFileReceived(GLOBAL.DEFAULT_OK_RESPONSE, thisObject); // Note that the call back is called for every file loaded at each cursor. - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] onFileReceived -> err = " + err); } - callBackWhenFileReceived(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function getFileCursor(pPeriod) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] getFileCursor -> Entering function."); } - - return fileCursors.get(pPeriod); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] getFileCursor -> err = " + err); } - - } - } - - function setDatetime(pDatetime) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] setDatetime -> Entering function."); } - - filesLoaded = 0; - expectedFiles = 0; - - fileCursors.forEach(setDatetimeToEach) - - function setDatetimeToEach(fileCursor, key, map) { - - fileCursor.setDatetime(pDatetime, onFileReceived); - expectedFiles = expectedFiles + fileCursor.getExpectedFiles(); - - } - - if (INFO_LOG === true) { logger.write("[INFO] setDatetime -> expectedFiles = " + expectedFiles); } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] setDatetime -> err = " + err); } - - } - } - - function setTimePeriod(pTimePeriod, pDatetime) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] setTimePeriod -> Entering function."); } - - filesLoaded = 0; - expectedFiles = 0; - - fileCursors.forEach(setTimePeriodToEach) - - function setTimePeriodToEach(fileCursor, key, map) { - - fileCursor.setTimePeriod(pTimePeriod, pDatetime, onFileReceived); - expectedFiles = expectedFiles + fileCursor.getExpectedFiles(); - - } - - if (INFO_LOG === true) { logger.write("[INFO] setTimePeriod -> expectedFiles = " + expectedFiles); } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> err = " + err); } - - } - } - - function getExpectedFiles() { - - if (INFO_LOG === true) { logger.write("[INFO] getExpectedFiles -> Entering function."); } - - return expectedFiles; - - } - - function getFilesLoaded() { - - if (INFO_LOG === true) { logger.write("[INFO] getFilesLoaded -> Entering function."); } - - return filesLoaded; - - } -} \ No newline at end of file diff --git a/Files/FileCloud.js b/Files/FileCloud.js deleted file mode 100644 index 83869cc1e..000000000 --- a/Files/FileCloud.js +++ /dev/null @@ -1,208 +0,0 @@ - -function newFileCloud() { - - const MODULE_NAME = "File Cloud"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - /* - - This is the module in the system that actually connects to the cloud storage and grabs from there the needed files. - - */ - - var thisObject = { - getFile: getFile, - initialize: initialize - }; - - let blobService; - - return thisObject; - - function initialize(pBot) { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> pBot = " + pBot.codeName); } - - blobService = AzureStorage.Blob.createBlobServiceWithSas(pBot.storage.fileUri, pBot.storage.sas); - - } - - function getFile(pDevTeam, pBot, pSet, pExchange, pMarket, pPeriodName, pDatetime, pSequence, pDataRange, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] getFile -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] getFile -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pSet.filePath + "-" + pSet.fileName); } - - let fileName; - let filePath; - - if (pDataRange === undefined) { - - fileName = pSet.fileName; - filePath = pSet.filePath; - - } else { - - if (pSet.dataRange !== undefined) { - - fileName = pSet.dataRange.fileName; - filePath = pSet.dataRange.filePath; - - } else { - - let customErr = { - result: GLOBAL.CUSTOM_FAIL_RESPONSE.result, - message: "Missing Configuration." - }; - - if (ERROR_LOG === true) { logger.write("[WARN] getFile -> onFileReceived -> customErr.message = " + customErr.message); } - if (ERROR_LOG === true) { logger.write("[WARN] getFile -> onFileReceived -> Data Range configuration could not be found. "); } - - callBackFunction(customErr); - return; - } - } - - if (fileName === undefined) { - - logger.write("[ERROR] getFile -> Inconsistant data. Check the following: "); - logger.write("[ERROR] getFile -> pDevTeam = " + JSON.stringify(pDevTeam)); - logger.write("[ERROR] getFile -> pBot = " + JSON.stringify(pBot)); - logger.write("[ERROR] getFile -> pSet = " + JSON.stringify(pSet)); - logger.write("[ERROR] getFile -> pExchange = " + JSON.stringify(pExchange)); - logger.write("[ERROR] getFile -> pMarket = " + JSON.stringify(pMarket)); - logger.write("[ERROR] getFile -> pPeriodName = " + JSON.stringify(pPeriodName)); - - throw ("Inconsistant data received."); - } - - if (pMarket !== undefined) { - - fileName = fileName.replace("@AssetA", pMarket.assetA); - fileName = fileName.replace("@AssetB", pMarket.assetB); - } - - if (pDevTeam !== undefined) { - - filePath = filePath.replace("@DevTeam", pDevTeam.codeName); - } - - if (pBot !== undefined) { - - filePath = filePath.replace("@Bot", pBot.codeName); - } - - if (pExchange !== undefined) { - - filePath = filePath.replace("@Exchange", pExchange.name); - } - - filePath = filePath.replace("@Period", pPeriodName); - - if (pDatetime !== undefined) { - - filePath = filePath.replace("@Year", pDatetime.getUTCFullYear()); - filePath = filePath.replace("@Month", pad(pDatetime.getUTCMonth() + 1, 2)); - filePath = filePath.replace("@Day", pad(pDatetime.getUTCDate(), 2)); - filePath = filePath.replace("@Hour", pad(pDatetime.getUTCHours(), 2)); - filePath = filePath.replace("@Minute", pad(pDatetime.getUTCMinutes(), 2)); - - } - - if (pSequence !== undefined) { - - fileName = fileName.replace("@Sequence", pSequence); - } - - if (INFO_LOG === true) { logger.write("[INFO] getFile -> filePath = " + filePath); } - if (INFO_LOG === true) { logger.write("[INFO] getFile -> fileName = " + fileName); } - - let containerName; - - if (pBot.codeName !== "AATom") { - containerName = pDevTeam.codeName.toLowerCase(); - } else { - containerName = 'data'; - } - - blobService.getBlobToText(containerName, filePath + "/" + fileName, onFileReceived); - - function onFileReceived(err, text, response) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] getFile -> onFileReceived -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] getFile -> onFileReceived -> filePath = " + filePath); } - if (INFO_LOG === true) { logger.write("[INFO] getFile -> onFileReceived -> fileName = " + fileName); } - - let data; - - if (err) { - - if (err.code === "BlobNotFound" | err.code === "FileNotFound" | err.code === "ParentNotFound") { - - let customErr = { - result: GLOBAL.CUSTOM_FAIL_RESPONSE.result, - message: "File does not exist." - }; - - if (INFO_LOG === true) { logger.write("[WARN] getFile -> onFileReceived -> customErr.message = " + customErr.message); } - - callBackFunction(customErr); - return; - } - - if (ERROR_LOG === true) { logger.write("[ERROR] getFile -> onFileReceived -> containerName = " + containerName); } - if (ERROR_LOG === true) { logger.write("[ERROR] getFile -> onFileReceived -> filePath = " + filePath); } - if (ERROR_LOG === true) { logger.write("[ERROR] getFile -> onFileReceived -> fileName = " + fileName); } - if (ERROR_LOG === true) { logger.write("[ERROR] getFile -> onFileReceived -> Unexpected Error Ocurred."); } - if (ERROR_LOG === true) { logger.write("[ERROR] getFile -> onFileReceived -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - - } else { - - try { - - data = JSON.parse(text); - - if (INFO_LOG === true) { logger.write("[INFO] getFile -> onFileReceived -> Data received is valid JSON."); } - - callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, data); - return; - - } catch (err) { - - if (INFO_LOG === true) { logger.write("[WARN] getFile -> onFileReceived -> err = " + err); } - - let customErr = { - result: GLOBAL.CUSTOM_OK_RESPONSE.result, - message: "Data not in JSON Format." - }; - - if (INFO_LOG === true) { logger.write("[WARN] getFile -> onFileReceived -> customErr.message = " + customErr.message); } - - callBackFunction(customErr, text); - return; - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] getFile -> onFileReceived -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] getFile -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } -} \ No newline at end of file diff --git a/Files/FileCursor.js b/Files/FileCursor.js deleted file mode 100644 index 4a65ac2ad..000000000 --- a/Files/FileCursor.js +++ /dev/null @@ -1,541 +0,0 @@ - -function newFileCursor() { - - const MODULE_NAME = "File Cursor"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let files = new Map; - let cursorDate; - - let thisObject = { - setDatetime: setDatetime, - setTimePeriod: setTimePeriod, - files: undefined, - getExpectedFiles: getExpectedFiles, - initialize: initialize - } - - thisObject.files = files; - - let minCursorSize = 10; - let maxCursorSize = 30; - - let market; - let exchange; - let fileCloud; - let devTeam; - let bot; - let thisSet; - let periodName; - let timePeriod; - let beginDateRange; - let endDateRange; - - return thisObject; - - function initialize(pFileCloud, pDevTeam, pBot, pSet, pExchange, pMarket, pPeriodName, pTimePeriod, pCursorDate, pCurrentTimePeriod, pBeginDateRange, pEndDateRange, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pSet.codeName + "-" + pPeriodName); } - - market = pMarket; - exchange = pExchange; - fileCloud = pFileCloud; - devTeam = pDevTeam; - bot = pBot; - thisSet = pSet; - periodName = pPeriodName; - cursorDate = pCursorDate; - timePeriod = pTimePeriod; - beginDateRange = pBeginDateRange; - endDateRange = pEndDateRange; - - setTimePeriod(pCurrentTimePeriod, pCursorDate, callBackFunction); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function setTimePeriod(pTimePeriod, pDatetime, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] setTimePeriod -> Entering function."); } - - /* - - We are implementing here an algorithm designed to save bandwidth, memory and processing power at the browser. - We say there is a saving mode where the cursor is running at a minimum size. When the end user aproaches the time period the cursor - is set, then it should exit the saving mode and go to its actual size. - - To do this we are going to measure the distance from the Time Period received to the one the cursors was initialized with. - If these periods are consecutive, it means that the cursor should exit saving mode and load its full size. - - */ - - cursorDate = pDatetime; // Adjust the cursor date to the one received. - - let positionA; - - for (let i = 0; i < dailyFilePeriods.length; i++) { - - let period = dailyFilePeriods[i]; - - if (period[0] === pTimePeriod) { - - positionA = i; - - } - - if (period[0] === timePeriod) { - - positionB = i; - - } - } - - if (Math.abs(positionB - positionA) <= 1) { - - exitSavingMode(); - //console.log("File Cursor with period " + periodName + " EXITED saving mode after being notified the user is at period " + convertTimePeriodToName(pTimePeriod)); - - } else { - - enterSavingMode(); - //console.log("File Cursor with period " + periodName + " ENTERED saving mode after being notified the user is at period " + convertTimePeriodToName(pTimePeriod)); - - } - - getFiles(callBackFunction); - - collectGarbage(callBackFunction); - - function enterSavingMode() { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] setTimePeriod -> enterSavingMode -> Entering function."); } - - switch (timePeriod) { - - case _45_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 30; - } - break; - case _40_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 20; - } - break; - case _30_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - case _20_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - case _15_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - case _10_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - case _5_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - case _4_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - case _3_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - case _2_MINUTES_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - case _1_MINUTE_IN_MILISECONDS: - { - minCursorSize = 3; - maxCursorSize = 15; - } - break; - default: - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> enterSavingMode -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function exitSavingMode() { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] setTimePeriod -> exitSavingMode -> Entering function."); } - - switch (timePeriod) { - - case _45_MINUTES_IN_MILISECONDS: - { - minCursorSize = 15; - maxCursorSize = 30; - } - break; - case _40_MINUTES_IN_MILISECONDS: - { - minCursorSize = 13; - maxCursorSize = 20; - } - break; - case _30_MINUTES_IN_MILISECONDS: - { - minCursorSize = 11; - maxCursorSize = 15; - } - break; - case _20_MINUTES_IN_MILISECONDS: - { - minCursorSize = 9; - maxCursorSize = 15; - } - break; - case _15_MINUTES_IN_MILISECONDS: - { - minCursorSize = 7; - maxCursorSize = 15; - } - break; - case _10_MINUTES_IN_MILISECONDS: - { - minCursorSize = 7; - maxCursorSize = 15; - } - break; - case _5_MINUTES_IN_MILISECONDS: - { - minCursorSize = 5; - maxCursorSize = 15; - } - break; - case _4_MINUTES_IN_MILISECONDS: - { - minCursorSize = 5; - maxCursorSize = 15; - } - break; - case _3_MINUTES_IN_MILISECONDS: - { - minCursorSize = 5; - maxCursorSize = 15; - } - break; - case _2_MINUTES_IN_MILISECONDS: - { - minCursorSize = 5; - maxCursorSize = 15; - } - break; - case _1_MINUTE_IN_MILISECONDS: - { - minCursorSize = 5; - maxCursorSize = 15; - } - break; - default: - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> exitSavingMode -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] setTimePeriod -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function setDatetime(pDatetime, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] setDatetime -> Entering function."); } - - if (pDatetime === undefined) { return; } - - cursorDate = pDatetime; - - getFiles(callBackFunction); - - collectGarbage(callBackFunction); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] setDatetime -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function getFiles(callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> Entering function."); } - - let i = 0; - let j = 0; - - let dateString; - - getNextFile(); - - function getNextFile() { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> getNextFile -> Entering function."); } - - let targetDate = new Date(cursorDate); - targetDate.setUTCDate(targetDate.getUTCDate() + j); - - /* Small algorith to allow load first the current date, then alternate between the most forwad and the most backwards ones. */ - if (j === 0) { j++; } - else { - if (j < 0) { - j = -j; - j++; - } - else { - j = -j; - } - } - - let compareTargetDate = new Date(targetDate.toDateString()); - - if (beginDateRange !== undefined) { - - let compareBeginDate = new Date(beginDateRange.toDateString()); - - if (compareTargetDate.valueOf() < compareBeginDate.valueOf()) { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> getNextFile -> compareTargetDate < compareBeginDate -> Not loading the file for this date."); } - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> getNextFile -> compareTargetDate < compareBeginDate -> compareTargetDate = " + compareTargetDate); } - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> getNextFile -> compareTargetDate < compareBeginDate -> compareBeginDate = " + compareBeginDate); } - - controlLoop(); - return; - } - } - - if (endDateRange !== undefined) { - - let compareEndDate = new Date(endDateRange.toDateString()); - - if (compareTargetDate.valueOf() > compareEndDate.valueOf()) { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> getNextFile -> compareTargetDate > compareEndDate -> Not loading the file for this date."); } - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> getNextFile -> compareTargetDate > compareEndDate -> compareTargetDate = " + compareTargetDate); } - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> getNextFile -> compareTargetDate > compareEndDate -> compareEndDate = " + compareEndDate); } - - controlLoop(); - return; - } - } - - dateString = targetDate.getUTCFullYear() + '-' + pad(targetDate.getUTCMonth() + 1, 2) + '-' + pad(targetDate.getUTCDate(), 2); - - let currentDay = Math.trunc((new Date()).valueOf() / (24 * 60 * 60 * 1000)); - let targetDay = Math.trunc(targetDate.valueOf() / (24 * 60 * 60 * 1000)); - - if (targetDay > currentDay) { - - controlLoop(); - - } else { - - if (thisObject.files.get(dateString) === undefined) { // We dont reload files we already have. - - fileCloud.getFile(devTeam, bot, thisSet, exchange, market, periodName, targetDate, undefined, undefined, onFileReceived); - - } else { - - controlLoop(); - } - - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] getFiles -> getNextFile -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function onFileReceived(err, file) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> onFileReceived -> Entering function."); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> onFileReceived -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> onFileReceived -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> onFileReceived -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> onFileReceived -> err.message = " + err.message); } - - if (err.message === "File does not exist.") { - - controlLoop(); - return; - - } else { - - callBackFunction(err); - return; - } - - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - thisObject.files.set(dateString, file); - - controlLoop(); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] getFiles -> onFileReceived -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function controlLoop() { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] getFiles -> controlLoop -> Entering function."); } - - if (callBackFunction !== undefined) { - - callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE); - - } - - i++; - - if (i < minCursorSize) { - - getNextFile(); - - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] getFiles -> controlLoop -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] getFiles -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function collectGarbage(callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] collectGarbage -> Entering function."); } - - date = removeTime(cursorDate); - - let minDate = date.valueOf() - maxCursorSize * ONE_DAY_IN_MILISECONDS / 2; - let maxDate = date.valueOf() + maxCursorSize * ONE_DAY_IN_MILISECONDS / 2; - - for (let key of thisObject.files.keys()) { - - let keyDate = new Date(key); - - if (keyDate.valueOf() < minDate || keyDate.valueOf() > maxDate) { - - thisObject.files.delete(key); - - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] collectGarbage -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - - } - - function getExpectedFiles() { - - if (INFO_LOG === true) { logger.write("[INFO] getExpectedFiles -> Entering function."); } - - return minCursorSize; - - } - -} \ No newline at end of file diff --git a/Files/FileSequence.js b/Files/FileSequence.js deleted file mode 100644 index 2665e0840..000000000 --- a/Files/FileSequence.js +++ /dev/null @@ -1,192 +0,0 @@ - -function newFileSequence() { - - const MODULE_NAME = "File Sequence"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let thisObject = { - getFile: getFile, - getExpectedFiles: getExpectedFiles, - getFilesLoaded: getFilesLoaded, - initialize: initialize - } - - let filesLoaded = 0; - - let fileCloud; - - let files = new Map; - - let maxSequence = -1; // This is replaced by the content of the sequence file, which contains an index that starts on zero. In the case that the sequence file is not found the default value is -1 sin when you add 1 it gives you the amount of files in the sequence, zero. - - return thisObject; - - function initialize(pDevTeam, pBot, pProduct, pSet, pExchange, pMarket, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - let exchange = ecosystem.getExchange(pProduct, pExchange); - - if (exchange === undefined) { - - throw "Exchange not supoorted by this pProduct of the ecosystem! - pDevTeam.codeName = " + pDevTeam.codeName + ", pBot.codeName = " + pBot.codeName + ", pProduct.codeName = " + pProduct.codeName + ", pExchange = " + pExchange; - - } - - fileCloud = newFileCloud(); - fileCloud.initialize(pBot); - - /* First we will get the sequence max number */ - - fileCloud.getFile(pDevTeam, pBot, pSet, exchange, pMarket, undefined, undefined, "Sequence", undefined, onSequenceFileReceived); - - function onSequenceFileReceived(err, file) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> Received CUSTOM OK Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> err.message = " + err.message); } - - callBackFunction(err); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> err.message = " + err.message); } - - callBackFunction(err, thisObject); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - maxSequence = Number(file); - - /* Now we will get the sequence of files */ - - for (let i = 0; i <= maxSequence; i++) { - - fileCloud.getFile(pDevTeam, pBot, pSet, exchange, pMarket, undefined, undefined, i, undefined, onFileReceived); - - function onFileReceived(err, file) { - - try { - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> onFileReceived -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> onFileReceived -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> onFileReceived -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> onFileReceived -> err.message = " + err.message); } - - callBackFunction(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSequenceFileReceived -> onFileReceived -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - files.set(i, file); - - filesLoaded++; - - callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, thisObject); // Note that the callback is called for every file loaded. - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onSequenceFileReceived -> onFileReceived -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onSequenceFileReceived -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function getFile(pSequence) { - - if (INFO_LOG === true) { logger.write("[INFO] getFile -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] getFile -> pSequence = " + pSequence); } - - return files.get(pSequence); - - } - - function getExpectedFiles() { - - if (INFO_LOG === true) { logger.write("[INFO] getExpectedFiles -> Entering function."); } - - return maxSequence + 1; - - } - - function getFilesLoaded() { - - if (INFO_LOG === true) { logger.write("[INFO] getFilesLoaded -> Entering function."); } - - return filesLoaded; - - } - -} \ No newline at end of file diff --git a/Files/MarketFiles.js b/Files/MarketFiles.js deleted file mode 100644 index cd8c20946..000000000 --- a/Files/MarketFiles.js +++ /dev/null @@ -1,138 +0,0 @@ - -function newMarketFiles() { - - const MODULE_NAME = "Market Files"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let thisObject = { - getFile: getFile, - getExpectedFiles: getExpectedFiles, - getFilesLoaded: getFilesLoaded, - initialize: initialize - } - - let filesLoaded = 0; - - let fileCloud; - - let files = new Map; - - return thisObject; - - function initialize(pDevTeam, pBot, pProduct, pSet, pExchange, pMarket, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - let exchange = ecosystem.getExchange(pProduct, pExchange); - - if (exchange === undefined) { - - throw "Exchange not supoorted by this pProduct of the ecosystem! - pDevTeam.codeName = " + pDevTeam.codeName + ", pBot.codeName = " + pBot.codeName + ", pProduct.codeName = " + pProduct.codeName + ", pExchange = " + pExchange; - - } - - fileCloud = newFileCloud(); - fileCloud.initialize(pBot); - - /* Now we will get the market files */ - - for (let i = 0; i < marketFilesPeriods.length; i++) { - - let periodTime = marketFilesPeriods[i][0]; - let periodName = marketFilesPeriods[i][1]; - - if (pSet.validPeriods.includes(periodName) === true) { - - fileCloud.getFile(pDevTeam, pBot, pSet, exchange, pMarket, periodName, undefined, undefined, undefined, onFileReceived); - - function onFileReceived(err, file) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Entering function."); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> err.message = " + err.message); } - - callBackFunction(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - files.set(periodTime, file); - - filesLoaded++; - - callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, thisObject); // Note that the callback is called for every file loaded. - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onFileReceived -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - } - } - - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function getFile(pPeriod) { - - if (INFO_LOG === true) { logger.write("[INFO] getFile -> Entering function."); } - - return files.get(pPeriod); - - } - - function getExpectedFiles() { - - if (INFO_LOG === true) { logger.write("[INFO] getExpectedFiles -> Entering function."); } - - return marketFilesPeriods.length; - - } - - function getFilesLoaded() { - - if (INFO_LOG === true) { logger.write("[INFO] getFilesLoaded -> Entering function."); } - - return filesLoaded; - - } - -} \ No newline at end of file diff --git a/Files/SingleFile.js b/Files/SingleFile.js deleted file mode 100644 index 38e4602c4..000000000 --- a/Files/SingleFile.js +++ /dev/null @@ -1,105 +0,0 @@ - -function newSingleFile() { - - const MODULE_NAME = "Single File"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let thisObject = { - getFile: getFile, - initialize: initialize - } - - let fileCloud; - let file; - - return thisObject; - - function initialize(pDevTeam, pBot, pProduct, pSet, pExchange, pMarket, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - let exchange = ecosystem.getExchange(pProduct, pExchange); - - if (pExchange !== undefined && exchange === undefined) { // We support no exchange as a parameter, but if provided, then it must be at the list of exchanges of that product. - - throw "Exchange not supoorted by this pProduct of the ecosystem! - pDevTeam.codeName = " + pDevTeam.codeName + ", pBot.codeName = " + pBot.codeName + ", pProduct.codeName = " + pProduct.codeName + ", pExchange = " + pExchange; - - } - - fileCloud = newFileCloud(); - fileCloud.initialize(pBot); - - /* Now we will get the file */ - - fileCloud.getFile(pDevTeam, pBot, pSet, exchange, pMarket, undefined, undefined, undefined, undefined, onFileReceived); - - function onFileReceived(err, pFile) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Entering function."); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> err.message = " + err.message); } - - callBackFunction(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileReceived -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - file = pFile; - - callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onFileReceived -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function getFile() { - - if (INFO_LOG === true) { logger.write("[INFO] getFile -> Entering function."); } - - return file; - - } - -} \ No newline at end of file diff --git a/FloatingSpace/FloatingLayer.js b/FloatingSpace/FloatingLayer.js deleted file mode 100644 index 4cbfa6c01..000000000 --- a/FloatingSpace/FloatingLayer.js +++ /dev/null @@ -1,810 +0,0 @@ - -function newFloatingLayer() { - - const MODULE_NAME = "Floating Layer"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let thisObject = { - addFloatingObject: addFloatingObject, - killFloatingObject: killFloatingObject, - getFloatingObject: getFloatingObject, - physicsLoop: physicsLoop, - isInside: isInside, - isInsideFloatingObject: isInsideFloatingObject, - changeTargetRepulsion: changeTargetRepulsion, - initialize: initialize - }; - - let visibleFloatingObjects = []; - let invisibleFloatingObjects = []; - let dyingFloatingObjects = []; - - let maxTargetRepulsionForce = 0.0005; - - return thisObject; - - function initialize(callBackFunction) { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - callBackFunction(); - } - - function addFloatingObject(pFloatingObject) { - - if (INFO_LOG === true) { logger.write("[INFO] addFloatingObject -> Entering function."); } - - invisibleFloatingObjects.push(pFloatingObject); - - if (INFO_LOG === true) { logger.write("[INFO] addFloatingObject -> invisibleFloatingObjects.length = " + invisibleFloatingObjects.length); } - - pFloatingObject.handle = Math.floor((Math.random() * 10000000) + 1); - - if (INFO_LOG === true) { logger.write("[INFO] addFloatingObject -> pFloatingObject.handle = " + pFloatingObject.handle); } - if (INFO_LOG === true) { logger.write("[INFO] addFloatingObject -> pFloatingObject.type = " + pFloatingObject.type); } - - } - - function killFloatingObject(pFloatingObjectHandle) { - - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> Entering function."); } - - for (let i = 0; i < invisibleFloatingObjects.length; i++) { - - let floatingObject = invisibleFloatingObjects[i]; - - if (floatingObject.handle === pFloatingObjectHandle) { - invisibleFloatingObjects.splice(i, 1); // Delete item from array. - - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> floatingObject.handle = " + floatingObject.handle); } - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> Removing floatingObject from invisibleFloatingObjects."); } - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> invisibleFloatingObjects.length = " + invisibleFloatingObjects.length); } - - sendToDie(floatingObject); - 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. - - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> floatingObject.handle = " + floatingObject.handle); } - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> Removing floatingObject from visibleFloatingObjects."); } - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> visibleFloatingObjects.length = " + visibleFloatingObjects.length); } - - sendToDie(floatingObject); - return; - } - } - - function sendToDie(pFloatingObject) { - - /* Lets mofigy the targerRadius */ - - pFloatingObject.targetRadius = 0; - - /* Lets transfer the payload to a new payload structure. */ - - let payload = {}; - - switch (pFloatingObject.type) { - - case "Profile Ball": { - - if (pFloatingObject.payload.profile.visible === false) { return;} - - payload.profile = { - position: { - x: pFloatingObject.payload.profile.position.x, - y: pFloatingObject.payload.profile.position.y - }, - visible: pFloatingObject.payload.profile.visible - } - - pFloatingObject.payload = payload; - break; - } - case "Note": { - - if (pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].visible === false) { return; } - - payload.notes = []; - let note = { - title: pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].title, - body: pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].body, - date: pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].date, - rate: pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].rate, - position: { - x: pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].position.x, - y: pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].position.y - }, - visible: pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].visible - }; - payload.notes.push(note); - pFloatingObject.payloadNoteIndex = 0; - - pFloatingObject.payload = payload; - break; - } - default: { - - break; - } - } - - dyingFloatingObjects.push(pFloatingObject); - - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> Adding floatingObject to dyingFloatingObjects."); } - if (INFO_LOG === true) { logger.write("[INFO] killFloatingObject -> dyingFloatingObjects.length = " + dyingFloatingObjects.length); } - } - } - - function getFloatingObject(pFloatingObjectHandle, pFloatingObjectIndex) { - - if (INFO_LOG === true) { logger.write("[INFO] getFloatingObject -> Entering function."); } - - 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; - } - } - } - } - - - /******************************************/ - /* */ - /* Physics Engine Follows */ - /* */ - /******************************************/ - - function physicsLoop() { - - /* This function makes all the calculations to apply phisycs on all floatingObjects in this space. */ - - for (let i = 0; i < visibleFloatingObjects.length; i++) { - - let floatingObject = visibleFloatingObjects[i]; - - /* Change position based on speed */ - - floatingObject.currentPosition.x = floatingObject.currentPosition.x + floatingObject.currentSpeed.x; - floatingObject.currentPosition.y = floatingObject.currentPosition.y + floatingObject.currentSpeed.y; - - /* Apply some friction to desacelerate */ - - floatingObject.currentSpeed.x = floatingObject.currentSpeed.x * floatingObject.friction; // Desaceleration factor. - floatingObject.currentSpeed.y = floatingObject.currentSpeed.y * floatingObject.friction; // Desaceleration factor. - - // Gives a minimun speed towards their taget. - - let payload = { - position: undefined, - visible: false - }; - - switch (floatingObject.type) { - - case "Profile Ball": { - - payload.position = floatingObject.payload.profile.position; - payload.visible = floatingObject.payload.profile.visible; - break; - } - case "Note": { - - payload.position = floatingObject.payload.notes[floatingObject.payloadNoteIndex].position; - payload.visible = floatingObject.payload.notes[floatingObject.payloadNoteIndex].visible; - - break; - } - default: { - - break; - } - } - - if (floatingObject.currentPosition.x < payload.position.x) { - floatingObject.currentSpeed.x = floatingObject.currentSpeed.x + .005; - } else { - floatingObject.currentSpeed.x = floatingObject.currentSpeed.x - .005; - } - - if (floatingObject.currentPosition.y < payload.position.y) { - floatingObject.currentSpeed.y = floatingObject.currentSpeed.y + .005; - } else { - floatingObject.currentSpeed.y = floatingObject.currentSpeed.y - .005; - } - - // Lets put a maximun speed also. - - const MAX_SPEED = 50; - - 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; - } - - // The radius also have a target. - - if (Math.abs(floatingObject.currentRadius - floatingObject.targetRadius) >= 1) { - - if (floatingObject.currentRadius < floatingObject.targetRadius) { - floatingObject.currentRadius = floatingObject.currentRadius + .5; - } else { - floatingObject.currentRadius = floatingObject.currentRadius - .5; - } - } - - // The imageSize also have a target. - - if (Math.abs(floatingObject.currentImageSize - floatingObject.targetImageSize) >= 1) { - - if (floatingObject.currentImageSize < floatingObject.targetImageSize) { - floatingObject.currentImageSize = floatingObject.currentImageSize + 1; - } else { - floatingObject.currentImageSize = floatingObject.currentImageSize - 1; - } - } - - - /* 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 */ - - currentRepulsionForce(i); - - targetRepulsionForce(i); - - gravityForce(floatingObject, payload); - - } - - /* We draw all the visibleFloatingObjects. */ - - 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[visibleFloatingObjects.length - i - 1]; - floatingObject.drawForeground(); - } - - /* Now we check if any of the created FloatingObjects where enabled to run under the Physics Engine. */ - - for (let i = 0; i < invisibleFloatingObjects.length; i++) { - - let floatingObject = invisibleFloatingObjects[i]; - - let payload = { - position: undefined, - visible: false - }; - - switch (floatingObject.type) { - - case "Profile Ball": { - - payload.position = floatingObject.payload.profile.position; - payload.visible = floatingObject.payload.profile.visible; - break; - } - case "Note": { - - payload.position = floatingObject.payload.notes[floatingObject.payloadNoteIndex].position; - payload.visible = floatingObject.payload.notes[floatingObject.payloadNoteIndex].visible; - break; - } - default: { - - break; - } - } - - if (payload.visible === true) { - - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> payload.visible = " + payload.visible); } - - /* The first time that the floatingObject becomes visible, we need to do this. */ - - floatingObject.radomizeCurrentPosition(payload.position); - floatingObject.radomizeCurrentSpeed(); - - visibleFloatingObjects.push(floatingObject); - - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> floatingObject.handle = " + floatingObject.handle); } - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> floatingObject added to visibleFloatingObjects"); } - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> visibleFloatingObjects.length = " + visibleFloatingObjects.length); } - - invisibleFloatingObjects.splice(i, 1); // Delete item from array. - - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> floatingObject.handle = " + floatingObject.handle); } - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> floatingObject removed from invisibleFloatingObjects"); } - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> invisibleFloatingObjects.length = " + invisibleFloatingObjects.length); } - - return; // Only one at the time. - - } - } - - /* Finally we check if any of the currently visible floatingObjects has become invisible and must be removed from the Physics Engine. */ - - for (let i = 0; i < visibleFloatingObjects.length; i++) { - - let floatingObject = visibleFloatingObjects[i]; - - let payload = { - position: undefined, - visible: true - }; - - switch (floatingObject.type) { - - case "Profile Ball": { - - payload.visible = floatingObject.payload.profile.visible; - break; - } - case "Note": { - - payload.visible = floatingObject.payload.notes[floatingObject.payloadNoteIndex].visible; - break; - } - default: { - - break; - } - } - - if (payload.visible === false) { - - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> payload.visible = " + payload.visible); } - - invisibleFloatingObjects.push(floatingObject); - - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> floatingObject.handle = " + floatingObject.handle); } - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> floatingObject added to invisibleFloatingObjects"); } - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> invisibleFloatingObjects.length = " + invisibleFloatingObjects.length); } - - visibleFloatingObjects.splice(i, 1); // Delete item from array. - - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> floatingObject.handle = " + floatingObject.handle); } - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> floatingObject removed from visibleFloatingObjects"); } - if (INFO_LOG === true) { logger.write("[INFO] physicsLoop -> visibleFloatingObjects.length = " + visibleFloatingObjects.length); } - - return; // Only one at the time. - - } - } - - /* We animate some parts of the dying objects */ - - for (let i = 0; i < dyingFloatingObjects.length; i++) { - - let floatingObject = dyingFloatingObjects[i]; - - if (Math.abs(floatingObject.currentRadius - floatingObject.targetRadius) >= 5) { - - let speed = Math.random(); - - floatingObject.currentRadius = floatingObject.currentRadius - speed * 3; - - } else { - - /* Here is when the floatingObjects are definetelly killed. */ - - dyingFloatingObjects.splice(i, 1); // Delete item from array. - break; // only one at the time. - } - } - - /* We also draw all the dyingFloatingObjects */ - - for (let i = 0; i < dyingFloatingObjects.length; i++) { - let floatingObject = dyingFloatingObjects[i]; - floatingObject.drawBackground(); - } - - for (let i = 0; i < dyingFloatingObjects.length; i++) { - let floatingObject = dyingFloatingObjects[dyingFloatingObjects.length - i - 1]; - floatingObject.drawForeground(); - } - } - - function gravityForce(floatingObject, payload) { - - /* 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 = .00001; - const minForce = 0.01; - - var d = Math.sqrt(Math.pow(payload.position.x - floatingObject.currentPosition.x, 2) + Math.pow(payload.position.y - floatingObject.currentPosition.y, 2)); // ... we calculate the distance ... - - var 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; - } - - var pos1 = { - x: floatingObject.currentPosition.x, - y: floatingObject.currentPosition.y - }; - - var pos2 = { - x: payload.position.x, - y: payload.position.y - }; - - var posDiff = { // Next we need the vector resulting from the 2 positions. - x: pos2.x - pos1.x, - y: pos2.y - pos1.y - }; - - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. - x: posDiff.x / d, - y: posDiff.y / d - }; - - var forceVector = { - x: unitVector.x * force, - y: unitVector.y * force - }; - - /* We add the force vector to the speed vector */ - - floatingObject.currentSpeed.x = floatingObject.currentSpeed.x + forceVector.x; - floatingObject.currentSpeed.y = floatingObject.currentSpeed.y + forceVector.y; - - } - - function currentRepulsionForce(currentFloatingObject) { - - /* We generate a repulsion force between floatingObjects, that prevents them to be collisioning so often. */ - - const coulomb = 2; - - var floatingObject1 = visibleFloatingObjects[currentFloatingObject]; - - for (var i = 0; i < visibleFloatingObjects.length; i++) { // The force to be applied is considering all other floatingObjects... - - if (i !== currentFloatingObject) { // ... except for the current one. - - var floatingObject2 = visibleFloatingObjects[i]; // So, for each floatingObject... - - var d = Math.sqrt(Math.pow(floatingObject2.currentPosition.x - floatingObject1.currentPosition.x, 2) + Math.pow(floatingObject2.currentPosition.y - floatingObject1.currentPosition.y, 2)); // ... we calculate the distance ... - - var 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; - } - - var pos1 = { - x: floatingObject1.currentPosition.x, - y: floatingObject1.currentPosition.y - }; - - var pos2 = { - x: floatingObject2.currentPosition.x, - y: floatingObject2.currentPosition.y - }; - - var posDiff = { // Next we need the vector resulting from the 2 positions. - x: pos2.x - pos1.x, - y: pos2.y - pos1.y - }; - - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. - x: posDiff.x / d, - y: posDiff.y / d - }; - - var forceVector = { - x: unitVector.x * force, - y: unitVector.y * force - }; - - /* We substract the force vector to the speed vector of the current floatingObject */ - - floatingObject1.currentSpeed.x = floatingObject1.currentSpeed.x - forceVector.x; - floatingObject1.currentSpeed.y = floatingObject1.currentSpeed.y - forceVector.y; - - } - - } - - } - - function targetRepulsionForce(currentFloatingObject) { - - /* We generate a repulsion force between floatingObjects, that prevents them to be collisioning so often. */ - - const coulomb = 2; - - var floatingObject1 = visibleFloatingObjects[currentFloatingObject]; - - for (var i = 0; i < visibleFloatingObjects.length; i++) { // The force to be applied is considering all other floatingObjects... - - var floatingObject2 = visibleFloatingObjects[i]; // So, for each floatingObject... - - let payload = { - position: undefined - }; - - switch (floatingObject2.type) { - - case "Profile Ball": { - - payload.position = floatingObject2.payload.profile.position; - break; - } - case "Note": { - - payload.position = floatingObject2.payload.notes[floatingObject2.payloadNoteIndex].position; - break; - } - default: { - - break; - } - } - - var d = Math.sqrt(Math.pow(payload.position.x - floatingObject1.currentPosition.x, 2) + Math.pow(payload.position.y - floatingObject1.currentPosition.y, 2)); // ... we calculate the distance ... - - var 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; - } - - var pos1 = { - x: floatingObject1.currentPosition.x, - y: floatingObject1.currentPosition.y - }; - - var pos2 = { - x: payload.position.x, - y: payload.position.y - }; - - var posDiff = { // Next we need the vector resulting from the 2 positions. - x: pos2.x - pos1.x, - y: pos2.y - pos1.y - }; - - var unitVector = { // To find the unit vector, we divide each component by the magnitude of the vector. - x: posDiff.x / d, - y: posDiff.y / d - }; - - var forceVector = { - x: unitVector.x * force, - y: unitVector.y * force - }; - - /* We substract the force vector to the speed vector of the current floatingObject */ - - floatingObject1.currentSpeed.x = floatingObject1.currentSpeed.x - forceVector.x; - floatingObject1.currentSpeed.y = floatingObject1.currentSpeed.y - forceVector.y; - - } - - } - - function changeTargetRepulsion(pDelta) { - - if (pDelta > 0) { - - pDelta = 1; - - } else { - - pDelta = -1; - - } - - maxTargetRepulsionForce = maxTargetRepulsionForce + pDelta / 10000; - - if (maxTargetRepulsionForce < 0.0001) { - - maxTargetRepulsionForce = 0.0001 - } - } - - function colliding(floatingObject1, floatingObject2) { - /* This function detects weather 2 floatingObjects collide with each other. */ - - var r1 = floatingObject1.currentRadius; - var r2 = floatingObject2.currentRadius; - - var distance = Math.sqrt(Math.pow(floatingObject2.currentPosition.x - floatingObject1.currentPosition.x, 2) + Math.pow(floatingObject2.currentPosition.y - floatingObject1.currentPosition.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; - } - - function isInside(x, y) { - - /* This function detects weather the point x,y is inside any of the floatingObjects. */ - - for (var i = 0; i < visibleFloatingObjects.length; i++) { - var floatingObject = visibleFloatingObjects[i]; - var distance = Math.sqrt(Math.pow(floatingObject.currentPosition.x - x, 2) + Math.pow(floatingObject.currentPosition.y - y, 2)); - - if (distance < floatingObject.currentRadius) { - return i; - } - } - return -1; - - } - - function isInsideFloatingObject(floatingObjectIndex, x, y) { - - /* This function detects weather the point x,y is inside one particular floatingObjects. */ - - - var floatingObject = visibleFloatingObjects[floatingObjectIndex]; - var distance = Math.sqrt(Math.pow(floatingObject.currentPosition.x - x, 2) + Math.pow(floatingObject.currentPosition.y - y, 2)); - - if (distance < floatingObject.currentRadius) { - return true; - } - - return false; - - } - - function distance(x1, y1, x2, y2) { - return Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2)); - } - - function resolveCollision(floatingObject1, floatingObject2) { - - /* This function changes speed and position of floatingObjects that are in collision */ - - var collisionision_angle = Math.atan2((floatingObject2.currentPosition.y - floatingObject1.currentPosition.y), (floatingObject2.currentPosition.x - floatingObject1.currentPosition.x)); - - var speed1 = Math.sqrt(floatingObject1.currentSpeed.x * floatingObject1.currentSpeed.x + floatingObject1.currentSpeed.y * floatingObject1.currentSpeed.y); // Magnitude of Speed Vector for floatingObject 1 - var speed2 = Math.sqrt(floatingObject2.currentSpeed.x * floatingObject2.currentSpeed.x + floatingObject2.currentSpeed.y * floatingObject2.currentSpeed.y); // Magnitude of Speed Vector for floatingObject 2 - - var direction_1 = Math.atan2(floatingObject1.currentSpeed.y, floatingObject1.currentSpeed.x); - var direction_2 = Math.atan2(floatingObject2.currentSpeed.y, floatingObject2.currentSpeed.x); - - var new_xspeed_1 = speed1 * Math.cos(direction_1 - collisionision_angle); - var new_yspeed_1 = speed1 * Math.sin(direction_1 - collisionision_angle); - var new_xspeed_2 = speed2 * Math.cos(direction_2 - collisionision_angle); - var new_yspeed_2 = speed2 * Math.sin(direction_2 - collisionision_angle); - - var final_xspeed_1 = ((floatingObject1.currentMass - floatingObject2.currentMass) * new_xspeed_1 + (floatingObject2.currentMass + floatingObject2.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass); - var final_xspeed_2 = ((floatingObject1.currentMass + floatingObject1.currentMass) * new_xspeed_1 + (floatingObject2.currentMass - floatingObject1.currentMass) * new_xspeed_2) / (floatingObject1.currentMass + floatingObject2.currentMass); - var final_yspeed_1 = new_yspeed_1; - var final_yspeed_2 = new_yspeed_2; - - var cosAngle = Math.cos(collisionision_angle); - var 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; - - var pos1 = { - x: floatingObject1.currentPosition.x, - y: floatingObject1.currentPosition.y - }; - - var pos2 = { - x: floatingObject2.currentPosition.x, - y: floatingObject2.currentPosition.y - }; - - // get the mtd - var posDiff = { - x: pos1.x - pos2.x, - y: pos1.y - pos2.y - }; - - var d = Math.sqrt(Math.pow(floatingObject2.currentPosition.x - floatingObject1.currentPosition.x, 2) + Math.pow(floatingObject2.currentPosition.y - floatingObject1.currentPosition.y, 2)); - - // minimum translation distance to push floatingObjects apart after intersecting - var scalar = (((floatingObject1.currentRadius + floatingObject2.currentRadius) - d) / d); - - var minTD = { - x: posDiff.x * scalar, - y: posDiff.y * scalar - }; - - // resolve intersection -- - // computing inverse mass quantities - var im1 = 1 / floatingObject1.currentMass; - var 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)); - - floatingObject1.currentPosition = pos1; - floatingObject2.currentPosition = pos2; - } -} - - diff --git a/FloatingSpace/FloatingObject.js b/FloatingSpace/FloatingObject.js deleted file mode 100644 index acff5c61b..000000000 --- a/FloatingSpace/FloatingObject.js +++ /dev/null @@ -1,192 +0,0 @@ - -function newFloatingObject() { - - var thisObject = { - - 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. - - physicsEnabled: false, - - initializeMass: initializeMass, - initializeRadius: initializeRadius, - initializeImageSize: initializeImageSize, - - imageId: undefined, - - currentPosition: 0, // Current x,y position of the floating object at the floating object's layer, where there is no displacement or zoom. This position is always changing towards the target position. - currentSpeed: 0, // This is the current speed of the floating object. - currentRadius: 0, // This is the current radius of the floating object, including its zoom applied. - 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. - - 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. - - fillStyle: '', - - labelStrokeStyle: '', - - radomizeCurrentPosition: radomizeCurrentPosition, - radomizeCurrentSpeed: radomizeCurrentSpeed, - - drawBackground: drawBackground, - drawForeground: drawForeground, - - updateMass: updateMass, // Function to update the mass when the zoom level changed. - updateRadius: updateRadius, // Function to update the radius when the zoom level changed. - - linkedObject: undefined, // This is a reference to the object that this floating object is representing. - linkedObjectType: "", // Since there might be floating objects for different types of objects, here we store the type of object we are linking to. - - container: undefined, // This is a pointer to the object where the floating object belongs to. - initialize: initialize - - }; - - let underlayingObject; - - return thisObject; - - function initialize(pType, callBackFunction) { - - switch (pType) { - - case "Profile Ball": { - - underlayingObject = newProfileBall(); - underlayingObject.initialize(onInitialized); - - function onInitialized(err) { - - - } - - break; - } - case "Note": { - - underlayingObject = newNote(); - underlayingObject.initialize(onInitialized); - - function onInitialized(err) { - - - } - break; - } - default: { - - break; - } - } - - thisObject.type = pType; - - callBackFunction(); - } - - function drawBackground() { - - underlayingObject.drawBackground(thisObject); - - } - - function drawForeground() { - - underlayingObject.drawForeground(thisObject); - - } - - function initializeMass(suggestedValue) { - - var mass = suggestedValue; - if (mass < 0.1) { - mass = 0.1; - } - - thisObject.rawMass = mass; - thisObject.currentMass = mass; - - } - - function initializeRadius(suggestedValue) { - - var radius = suggestedValue; - if (radius < 2) { - radius = 2; - } - - thisObject.rawRadius = radius; - thisObject.targetRadius = radius; - thisObject.currentRadius = radius / 3; - - } - - function initializeImageSize(suggestedValue) { - - var size = suggestedValue; - if (size < 2) { - size = 2; - } - - thisObject.rawImageSize = size; - thisObject.targetImageSize = size; - thisObject.currentImageSize = size / 3; - - } - - function radomizeCurrentPosition(arroundPoint) { - - var position = { - x: Math.floor((Math.random() * (200) - 100)) + arroundPoint.x, - y: Math.floor((Math.random() * (200) - 100)) + arroundPoint.y - }; - - thisObject.currentPosition = position; - } - - 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() { - - //thisObject.currentMass = thisObject.rawMass + thisObject.rawMass * thisObject.container.zoom.incrementM * thisObject.container.zoom.levelM; - - } - - function updateRadius() { - - //thisObject.targetRadius = thisObject.rawRadius + thisObject.rawRadius * thisObject.container.zoom.incrementR * thisObject.container.zoom.levelR; - - } -} - - - diff --git a/FloatingSpace/Note.js b/FloatingSpace/Note.js deleted file mode 100644 index 1e13559b2..000000000 --- a/FloatingSpace/Note.js +++ /dev/null @@ -1,309 +0,0 @@ - -function newNote() { - - var thisObject = { - - drawBackground: drawBackground, - drawForeground: drawForeground, - initialize: initialize - - }; - - return thisObject; - - function initialize(callBackFunction) { - - callBackFunction(); - - } - - function drawBackground(pFloatingObject) { - - if (pFloatingObject.currentRadius > 1) { - - /* Target Line */ - - browserCanvasContext.beginPath(); - browserCanvasContext.moveTo(pFloatingObject.currentPosition.x, pFloatingObject.currentPosition.y); - browserCanvasContext.lineTo(pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].position.x, pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].position.y); - browserCanvasContext.strokeStyle = 'rgba(204, 204, 204, 0.5)'; - browserCanvasContext.setLineDash([4, 2]); - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - browserCanvasContext.setLineDash([0, 0]); - - } - - if (pFloatingObject.currentRadius > 0.5) { - - /* Target Spot */ - - var radius = 1; - - browserCanvasContext.beginPath(); - browserCanvasContext.arc(pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].position.x, pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].position.y, radius, 0, Math.PI * 2, true); - browserCanvasContext.closePath(); - browserCanvasContext.fillStyle = 'rgba(30, 30, 30, 1)'; - browserCanvasContext.fill(); - - } - } - - function drawForeground(pFloatingObject) { - - const BUBBLE_CORNERS_RADIOUS = 5; - const TITLE_BAR_HEIGHT = 14; - - const BUBBLE_WIDTH = BUBBLE_CORNERS_RADIOUS + pFloatingObject.currentRadius * 4; - const BUBBLE_HEIGHT = BUBBLE_CORNERS_RADIOUS + pFloatingObject.currentRadius * 2; - - let borderPoint1; - let borderPoint2; - let borderPoint3; - let borderPoint4; - - let intialDisplace = { - x: pFloatingObject.currentPosition.x - BUBBLE_WIDTH / 2, - y: pFloatingObject.currentPosition.y - BUBBLE_HEIGHT / 2 - } - - if (pFloatingObject.currentRadius > 5) { - - /* Rounded Background */ - - borderPoint1 = { - x: intialDisplace.x + BUBBLE_CORNERS_RADIOUS, - y: intialDisplace.y + BUBBLE_CORNERS_RADIOUS - }; - - borderPoint2 = { - x: intialDisplace.x + BUBBLE_WIDTH - BUBBLE_CORNERS_RADIOUS, - y: intialDisplace.y + BUBBLE_CORNERS_RADIOUS - }; - - borderPoint3 = { - x: intialDisplace.x + BUBBLE_WIDTH - BUBBLE_CORNERS_RADIOUS, - y: intialDisplace.y + BUBBLE_HEIGHT - BUBBLE_CORNERS_RADIOUS - }; - - borderPoint4 = { - x: intialDisplace.x + BUBBLE_CORNERS_RADIOUS, - y: intialDisplace.y + + BUBBLE_HEIGHT - BUBBLE_CORNERS_RADIOUS - }; - - titleBarPoint1 = { - x: intialDisplace.x + 0, - y: intialDisplace.y + TITLE_BAR_HEIGHT - }; - - titleBarPoint2 = { - x: intialDisplace.x + BUBBLE_WIDTH, - y: intialDisplace.y + TITLE_BAR_HEIGHT - }; - - /* We paint the panel background first */ - - browserCanvasContext.fillStyle = 'rgba(255, 249, 196, 0.75)'; - browserCanvasContext.beginPath(); - - browserCanvasContext.arc(borderPoint1.x, borderPoint1.y, BUBBLE_CORNERS_RADIOUS, 1.0 * Math.PI, 1.5 * Math.PI); - browserCanvasContext.lineTo(borderPoint2.x, borderPoint2.y - BUBBLE_CORNERS_RADIOUS); - browserCanvasContext.arc(borderPoint2.x, borderPoint2.y, BUBBLE_CORNERS_RADIOUS, 1.5 * Math.PI, 2.0 * Math.PI); - browserCanvasContext.lineTo(borderPoint3.x + BUBBLE_CORNERS_RADIOUS, borderPoint3.y); - browserCanvasContext.arc(borderPoint3.x, borderPoint3.y, BUBBLE_CORNERS_RADIOUS, 0 * Math.PI, 0.5 * Math.PI); - browserCanvasContext.lineTo(borderPoint4.x, borderPoint4.y + BUBBLE_CORNERS_RADIOUS); - browserCanvasContext.arc(borderPoint4.x, borderPoint4.y, BUBBLE_CORNERS_RADIOUS, 0.5 * Math.PI, 1.0 * Math.PI); - browserCanvasContext.lineTo(borderPoint1.x - BUBBLE_CORNERS_RADIOUS, borderPoint1.y); - - browserCanvasContext.closePath(); - - browserCanvasContext.fill(); - - browserCanvasContext.lineWidth = 0.1; - browserCanvasContext.strokeStyle = 'rgba(54, 54, 54, 0.75)'; - browserCanvasContext.stroke(); - - /* We paint the title bar now */ - - browserCanvasContext.fillStyle = 'rgba(244, 224, 44, 0.75)'; - browserCanvasContext.beginPath(); - - browserCanvasContext.moveTo(titleBarPoint1.x, titleBarPoint1.y); - browserCanvasContext.lineTo(borderPoint1.x - BUBBLE_CORNERS_RADIOUS, borderPoint1.y); - browserCanvasContext.arc(borderPoint1.x, borderPoint1.y, BUBBLE_CORNERS_RADIOUS, 1.0 * Math.PI, 1.5 * Math.PI); - browserCanvasContext.lineTo(borderPoint2.x, borderPoint2.y - BUBBLE_CORNERS_RADIOUS); - browserCanvasContext.arc(borderPoint2.x, borderPoint2.y, BUBBLE_CORNERS_RADIOUS, 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(12, 64, 148, 0.75)'; - browserCanvasContext.stroke(); - - } - - if (pFloatingObject.currentRadius > 0.5) { - - /* Image */ - - let imagePosition = { - x: pFloatingObject.currentPosition.x, - y: pFloatingObject.currentPosition.y + BUBBLE_HEIGHT / 2 - }; - - if (pFloatingObject.payloadImageId !== undefined) { - - let image = document.getElementById(pFloatingObject.payloadImageId); - - if (image !== null) { - - browserCanvasContext.drawImage(image, imagePosition.x - pFloatingObject.currentImageSize / 2, imagePosition.y - pFloatingObject.currentImageSize / 2, pFloatingObject.currentImageSize, pFloatingObject.currentImageSize); - - } - } - - /* Image Contourn */ - - browserCanvasContext.beginPath(); - browserCanvasContext.arc(imagePosition.x, imagePosition.y, pFloatingObject.currentImageSize / 2, 0, Math.PI * 2, true); - browserCanvasContext.closePath(); - browserCanvasContext.strokeStyle = 'rgba(30, 30, 30, 0.25)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - /* Labels */ - - if (pFloatingObject.currentRadius > 6) { - - browserCanvasContext.strokeStyle = pFloatingObject.labelStrokeStyle; - - const SIZE_PERCENTAGE = Math.trunc(pFloatingObject.currentRadius / pFloatingObject.targetRadius * 100) / 100; - let ALPHA; - - if (pFloatingObject.targetRadius > 0) { - - ALPHA = 0.5 - (1 - SIZE_PERCENTAGE) * 5; - - } else { // Object is dying... - - ALPHA = Math.trunc((0.5 - (100 - pFloatingObject.currentRadius / 100) ) * 100) / 100; - - if (ALPHA < 0) { ALPHA = 0} - - } - - let labelPoint; - let fontSize = 10; - - let label; - - /* print the title */ - - label = pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].title; - - if (label !== undefined) { - - if (SIZE_PERCENTAGE > 0.9) { - - browserCanvasContext.font = fontSize + 'px Courier New'; - - let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; - let yOffset = (TITLE_BAR_HEIGHT - fontSize) / 2 + 2; - - labelPoint = { - x: intialDisplace.x + BUBBLE_WIDTH / 2 - xOffset, - y: intialDisplace.y + TITLE_BAR_HEIGHT - yOffset - }; - - browserCanvasContext.fillStyle = 'rgba(30, 30, 30, ' + ALPHA + ')' - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - } - } - - /* Message Body */ - - label = pFloatingObject.payload.notes[pFloatingObject.payloadNoteIndex].body; - - if (label !== undefined) { - - const WORDS_PER_ROW = 5; - const TOTAL_ROWS = 5; - - if (SIZE_PERCENTAGE > 0.9) { - - let rawLabelArray = label.split(" "); - let labelArray = []; - - /* Lets check when words are to long we add an empty space to the same line so as to roll all other words forward. */ - - for (let i = 0; i < rawLabelArray.length; i++) { - - let word = rawLabelArray[i]; - - labelArray.push(word); - - if (word.length > 8) { - labelArray.push(""); - } - - if (word.length > 10) { - labelArray.push(""); - } - } - - /* Calculate each ROW */ - - let labelRows = []; - - for (let i = 0; i < TOTAL_ROWS; i++) { - - let labelRow = ""; - - for (let j = 0; j < WORDS_PER_ROW; j++) { - - let newWord = labelArray[j + i * WORDS_PER_ROW]; - - if (newWord !== undefined && newWord !== "") { - - labelRow = labelRow + " " + newWord; - } - } - - if (labelRow !== "") { - - labelRows.push(labelRow); - - } - - } - - /* Now we plot each row. */ - - for (let i = 0; i < labelRows.length; i++) { - - let labelRow = labelRows[i]; - - let startingPosition = { - x: pFloatingObject.currentPosition.x, - y: pFloatingObject.currentPosition.y - labelRows.length / 2 * (fontSize * 0.60 + 10) - }; - - labelPoint = { - x: startingPosition.x - labelRow.length / 2 * fontSize * 0.60, - y: startingPosition.y + (i + 1) * (fontSize * 0.60 + 10) - }; - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, ' + ALPHA + ')' - browserCanvasContext.fillText(labelRow, labelPoint.x, labelPoint.y); - - } - } - } - } - } - } -} diff --git a/FloatingSpace/NoteSets.js b/FloatingSpace/NoteSets.js deleted file mode 100644 index 75c051611..000000000 --- a/FloatingSpace/NoteSets.js +++ /dev/null @@ -1,268 +0,0 @@ - -function newNoteSets() { - - const MODULE_NAME = "Note Sets"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let thisObject = { - createNoteSet: createNoteSet, - destroyNoteSet: destroyNoteSet, - initialize: initialize - }; - - let noteSets = []; - let floatingLayer; - - return thisObject; - - function initialize(pFloatingLayer, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - floatingLayer = pFloatingLayer; - - callBackFunction(GLOBAL.CUSTOM_OK_RESPONSE); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function createNoteSet(pPayload, pEventHandler, callBackFunction) { - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> Entering function."); } - - let noteSet = { - imageId: pPayload.profile.imageId, - floatingNotes: [], - payload: pPayload, - handle: Math.floor((Math.random() * 10000000) + 1) - }; - - noteSets.push(noteSet); - - pEventHandler.listenToEvent("Notes Changed", onNotesChanged, noteSets.length - 1) - - callBackFunction(GLOBAL.CUSTOM_OK_RESPONSE, noteSet.handle); - - function onNotesChanged(pNewNotes, pNoteSetIndex) { - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> pNewNotes.length = " + pNewNotes.length); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> pNoteSetIndex = " + pNoteSetIndex); } - - let noteSet = noteSets[pNoteSetIndex]; - let found = false; - let newFloatingNotes; - - /* First we remove the old notes. */ - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes."); } - - newFloatingNotes = []; // We will create a new array of Floating Notes as a result of this operation. - - for (let j = 0; j < noteSet.floatingNotes.length; j++) { - - found = false; - - let floatingNote = noteSet.floatingNotes[j]; - let floatingNoteKey = floatingNote.date.toString() + floatingNote.rate.toString(); - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> j = " + j); } - - let i; - - for (i = 0; i < pNewNotes.length; i++) { - - let plotterNote = pNewNotes[i]; - let plotterNoteKey = plotterNote.date.toString() + plotterNote.rate.toString(); - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> i = " + i); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> floatingNoteKey = " + floatingNoteKey); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> plotterNoteKey = " + plotterNoteKey); } - - if (plotterNoteKey === floatingNoteKey) { - - found = true; - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> found = " + found); } - break; - } - } - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> End of For i."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> found = " + found); } - - if (found === false) { - - floatingLayer.killFloatingObject(floatingNote.floatingHandle); - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> floatingNote.floatingHandle = " + floatingNote.floatingHandle); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> Removed from Layer."); } - - } else { - - newFloatingNotes.push(floatingNote); - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> floatingNote.floatingHandle = " + floatingNote.floatingHandle); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> Added to newFloatingNotes."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> newFloatingNotes.length = " + newFloatingNotes.length); } - - let floatingObject = floatingLayer.getFloatingObject(floatingNote.floatingHandle); - floatingObject.payloadNoteIndex = i; - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> floatingObject index updated."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> floatingObject.payloadNoteIndex = " + i); } - - } - } - - noteSet.floatingNotes = newFloatingNotes; - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Remove old Notes -> noteSet.floatingNotes.length = " + noteSet.floatingNotes.length); } - - /* Second we add the new notes. */ - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes."); } - - newFloatingNotes = []; // We will create a new array of Floating Notes as a result of this operation. - - for (let i = 0; i < pNewNotes.length; i++) { - - found = false; - - let plotterNote = pNewNotes[i]; - let plotterNoteKey = plotterNote.date.toString() + plotterNote.rate.toString(); - let floatingNote; - let floatingNoteKey; - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> i = " + i); } - - for (let j = 0; j < noteSet.floatingNotes.length; j++) { - - floatingNote = noteSet.floatingNotes[j]; - floatingNoteKey = floatingNote.date.toString() + floatingNote.rate.toString(); - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> j = " + j); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> floatingNoteKey = " + floatingNoteKey); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> plotterNoteKey = " + plotterNoteKey); } - - if (plotterNoteKey === floatingNoteKey) { - - found = true; - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> found = " + found); } - break; - } - } - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> End of For j."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> found = " + found); } - - if (found === false) { - - let floatingObject = newFloatingObject(); - floatingObject.initialize("Note", onInitialized); - - function onInitialized(err) { - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> Entering function."); } - - floatingObject.payload = noteSet.payload; - floatingObject.payloadNoteIndex = i; - floatingObject.payloadImageId = noteSet.imageId; - - floatingObject.friction = .995; - - floatingObject.initializeMass(200); - - //let bodyText = pNewNotes[i].body; - let radius; - - /* - if (bodyText.length < 100) { - radius = 100; - } else { - radius = bodyText.length; - } - */ - radius = 50; - - floatingObject.initializeRadius(radius); - floatingObject.initializeImageSize(15); - - floatingObject.fillStyle = 'rgba(255, 255, 255, 0.5)'; - floatingObject.labelStrokeStyle = 'rgba(60, 60, 60, 0.50)'; - - floatingLayer.addFloatingObject(floatingObject); - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> New Note added to Layer."); } - - plotterNote.floatingHandle = floatingObject.handle; - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> plotterNote.floatingHandle = " + plotterNote.floatingHandle); } - - newFloatingNotes.push(plotterNote); - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> Added to newFloatingNotes."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> newFloatingNotes.length = " + newFloatingNotes.length); } - - } - - } else { - - newFloatingNotes.push(floatingNote); - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> Old Note Kept."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> floatingNote.floatingHandle = " + floatingNote.floatingHandle); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> Added to newFloatingNotes."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> newFloatingNotes.length = " + newFloatingNotes.length); } - - let floatingObject = floatingLayer.getFloatingObject(floatingNote.floatingHandle); - floatingObject.payloadNoteIndex = i; - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> floatingObject index updated."); } - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> onInitialized -> floatingObject.payloadNoteIndex = " + i); } - - } - } - - noteSet.floatingNotes = newFloatingNotes; - - if (INFO_LOG === true) { logger.write("[INFO] createNoteSet -> onNotesChanged -> Add new Notes -> noteSet.floatingNotes.length = " + noteSet.floatingNotes.length); } - - } - } - - function destroyNoteSet(pHandle) { - - if (INFO_LOG === true) { logger.write("[INFO] destroyNoteSet -> Entering function."); } - - for (let i = 0; i < noteSets.length; i++) { - - let noteSet = noteSets[i]; - - if (noteSet.handle === pHandle) { - - for (let j = 0; j < noteSet.floatingNotes.length; j++) { - - let floatingNote = noteSet.floatingNotes[j]; - - floatingLayer.killFloatingObject(floatingNote.floatingHandle); - } - - noteSets.splice(i, 1); // Delete item from array. - return; - } - } - } -} - - diff --git a/FloatingSpace/ProfileBall.js b/FloatingSpace/ProfileBall.js deleted file mode 100644 index 87b1d30be..000000000 --- a/FloatingSpace/ProfileBall.js +++ /dev/null @@ -1,151 +0,0 @@ - -function newProfileBall() { - - var thisObject = { - - drawBackground: drawBackground, - drawForeground: drawForeground, - initialize: initialize - - }; - - return thisObject; - - function initialize(callBackFunction) { - - callBackFunction(); - - } - - function drawBackground(pFloatingObject) { - - if (pFloatingObject.currentRadius > 1) { - - /* Target Line */ - - browserCanvasContext.beginPath(); - browserCanvasContext.moveTo(pFloatingObject.currentPosition.x, pFloatingObject.currentPosition.y); - browserCanvasContext.lineTo(pFloatingObject.payload.profile.position.x, pFloatingObject.payload.profile.position.y); - browserCanvasContext.strokeStyle = 'rgba(204, 204, 204, 0.5)'; - browserCanvasContext.setLineDash([4, 2]); - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - browserCanvasContext.setLineDash([0, 0]); - - } - - if (pFloatingObject.currentRadius > 0.5) { - - /* Target Spot */ - - var radius = 1; - - browserCanvasContext.beginPath(); - browserCanvasContext.arc(pFloatingObject.payload.profile.position.x, pFloatingObject.payload.profile.position.y, radius, 0, Math.PI * 2, true); - browserCanvasContext.closePath(); - browserCanvasContext.fillStyle = 'rgba(30, 30, 30, 1)'; - browserCanvasContext.fill(); - - } - } - - function drawForeground(pFloatingObject) { - - if (pFloatingObject.currentRadius > 5) { - - /* Contourn */ - - browserCanvasContext.beginPath(); - browserCanvasContext.arc(pFloatingObject.currentPosition.x, pFloatingObject.currentPosition.y, pFloatingObject.currentRadius, 0, Math.PI * 2, true); - browserCanvasContext.closePath(); - browserCanvasContext.strokeStyle = 'rgba(30, 30, 30, 0.75)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - } - - if (pFloatingObject.currentRadius > 0.5) { - - /* Main FloatingObject */ - - var alphaA; - - if (pFloatingObject.currentRadius < 3) { - alphaA = 1; - } else { - alphaA = 0.75; - } - - alphaA = 0.75; - - browserCanvasContext.beginPath(); - browserCanvasContext.arc(pFloatingObject.currentPosition.x, pFloatingObject.currentPosition.y, pFloatingObject.currentRadius, 0, Math.PI * 2, true); - browserCanvasContext.closePath(); - - browserCanvasContext.fillStyle = pFloatingObject.fillStyle; - - browserCanvasContext.fill(); - - } - - /* Image */ - - if (pFloatingObject.payload.profile.imageId !== undefined) { - - let image = document.getElementById(pFloatingObject.payload.profile.imageId); - - if (image !== null) { - - browserCanvasContext.drawImage(image, pFloatingObject.currentPosition.x - pFloatingObject.currentImageSize / 2, pFloatingObject.currentPosition.y - pFloatingObject.currentImageSize / 2, pFloatingObject.currentImageSize, pFloatingObject.currentImageSize); - - } - } - - /* Label Text */ - - if (pFloatingObject.currentRadius > 6) { - - browserCanvasContext.strokeStyle = pFloatingObject.labelStrokeStyle; - - let labelPoint; - let fontSize = 10; - - browserCanvasContext.font = fontSize + 'px Courier New'; - - let label; - - label = pFloatingObject.payload.profile.upLabel; - - if (label !== undefined) { - - labelPoint = { - x: pFloatingObject.currentPosition.x - label.length / 2 * fontSize * 0.60, - y: pFloatingObject.currentPosition.y - pFloatingObject.currentImageSize / 2 - fontSize * 0.60 - 5 - }; - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = pFloatingObject.labelStrokeStyle; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - } - - label = pFloatingObject.payload.profile.downLabel; - - if (label !== undefined) { - - labelPoint = { - x: pFloatingObject.currentPosition.x - label.length / 2 * fontSize * 0.60, - y: pFloatingObject.currentPosition.y + pFloatingObject.currentImageSize / 2 + fontSize * 0.60 + 10 - }; - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = pFloatingObject.labelStrokeStyle; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - } - } - } -} - - - diff --git a/FloatingSpace/ProfileBalls.js b/FloatingSpace/ProfileBalls.js deleted file mode 100644 index 47c2e8761..000000000 --- a/FloatingSpace/ProfileBalls.js +++ /dev/null @@ -1,76 +0,0 @@ - -function newProfileBalls() { - - const MODULE_NAME = "Profile Balls"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - /* - - This object deals with Profile Balls, a type of Floating Object that shows profile info in a floating ball. - - */ - - let thisObject = { - - createNewProfileBall: createNewProfileBall, - destroyProfileBall: destroyProfileBall, - initialize: initialize - - } - - let floatingLayer; - - return thisObject; - - function initialize(pFloatingLayer, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - floatingLayer = pFloatingLayer; - - callBackFunction(GLOBAL.CUSTOM_OK_RESPONSE); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function createNewProfileBall(pPayload, callBackFunction) { - - let floatingObject = newFloatingObject(); - floatingObject.initialize("Profile Ball", onInitialized); - - function onInitialized(err) { - - floatingObject.payload = pPayload; - - floatingObject.friction = .995; - - floatingObject.initializeMass(100); - floatingObject.initializeRadius(30); - floatingObject.initializeImageSize(50); - - floatingObject.fillStyle = 'rgba(255, 255, 255, 0.5)'; - floatingObject.labelStrokeStyle = 'rgba(60, 60, 60, 0.50)'; - - floatingLayer.addFloatingObject(floatingObject); - - callBackFunction(GLOBAL.CUSTOM_OK_RESPONSE, floatingObject.handle); - - } - } - - function destroyProfileBall(pFloatingObjectHandle) { - - floatingLayer.killFloatingObject(pFloatingObjectHandle); - - } -} - diff --git a/Frame.js b/Frame.js deleted file mode 100644 index 14e314142..000000000 --- a/Frame.js +++ /dev/null @@ -1,590 +0,0 @@ - -function newFrame() { - - const PANEL_CORNERS_RADIOUS = 5; - const TITLE_BAR_HEIGHT = 14; // this must be grater than radius - - var frame = { - containerName: "", // This is for debugging purposes only. - parentFrame: undefined, // Here we store the parent cointainer zoom object. - width: browserCanvas.width, - height: browserCanvas.height, - position: undefined, - getBodyHeight: getBodyHeight, - draw: draw, - container: undefined, - frameThisPoint: frameThisPoint, // This function changes the actual frame coordinate system to the screen coordinate system. - unframeThisPoint: unframeThisPoint, - fitIntoFrame: fitIntoFrame, - isThisPointHere: isThisPointHere, // This function return true is the point received as parameter lives within this frame. - isInViewPort: isInViewPort, - canYouMoveHere: canYouMoveHere, - canYouZoomHere: canYouZoomHere, - initialize: initialize - }; - - return frame; - - function initialize() { - - var position = { - x: 0, - y: 0 - }; - - this.position = position; - } - - - function getBodyHeight() { - - return frame.height - TITLE_BAR_HEIGHT; - - } - - function isInViewPort() { - - point1 = { - x: 0, - y: 0 - }; - - point3 = { - x: frame.width, - y: frame.height - }; - - - /* Now the transformations. */ - - point1 = transformThisPoint(point1, frame.container); - point3 = transformThisPoint(point3, frame.container); - - if (point1.x < viewPort.visibleArea.topRight.x && point1.y < viewPort.visibleArea.bottomRight.y && point3.x > viewPort.visibleArea.bottomLeft.x && point3.y > viewPort.visibleArea.topLeft.y ) { - - return true; - } else { - return false; - } - - } - - function fitIntoFrame(point) { - - - /* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */ - - if (point.x > frame.width) { - point.x = frame.width; - } - - if (point.x < 0) { - point.x = 0; - } - - if (point.y > frame.height) { - point.y = frame.height; - } - - if (point.y < 0) { - point.y = 0; - } - - return point; - } - - - - function frameThisPoint(point) { - - if (this.parentFrame !== undefined) { - - point = this.parentFrame.frameThisPoint(point); - - } - - point.x = point.x + frame.position.x; - point.y = point.y + frame.position.y; - - return point; - } - - function unframeThisPoint(point) { - - if (this.parentFrame !== undefined) { - - point = this.parentFrame.unframeThisPoint(point); - - } - - point.x = point.x - this.position.x; - point.y = point.y - this.position.y; - - return point; - } - - function canYouMoveHere(tempDisplacement) { - - /* - - The current frame has a position and a displacement. We need to check that none of the boundaries points of the frame fall outside of its container frame. - First we apply the theoreticall displacement. - - */ - - point1 = { - x: this.position.x, - y: this.position.y - }; - - point2 = { - x: this.position.x + this.width, - y: this.position.y - }; - - point3 = { - x: this.position.x + this.width, - y: this.position.y + this.height - }; - - point4 = { - x: this.position.x, - y: this.position.y + this.height - }; - - /* Now the transformations. */ - - if (this.parentFrame !== undefined) { // If there is not a parent then there is no point to check bounderies. - - point1 = this.parentFrame.frameThisPoint(point1); - point2 = this.parentFrame.frameThisPoint(point2); - point3 = this.parentFrame.frameThisPoint(point3); - point4 = this.parentFrame.frameThisPoint(point4); - - /* We apply the temporary displacement. */ - - point1 = tempDisplacement.displaceThisPoint(point1); - point2 = tempDisplacement.displaceThisPoint(point2); - point3 = tempDisplacement.displaceThisPoint(point3); - point4 = tempDisplacement.displaceThisPoint(point4); - - /* We add the actual displacement. */ - - point1 = this.container.displacement.displaceThisPoint(point1); - point2 = this.container.displacement.displaceThisPoint(point2); - point3 = this.container.displacement.displaceThisPoint(point3); - point4 = this.container.displacement.displaceThisPoint(point4); - - if (this.parentFrame.isThisPointHere(point1) === false) { - return false; - } - - if (this.parentFrame.isThisPointHere(point2) === false) { - return false; - } - - if (this.parentFrame.isThisPointHere(point3) === false) { - return false; - } - - if (this.parentFrame.isThisPointHere(point4) === false) { - return false; - } - - } - - return true; - } - - function canYouZoomHere(tempZoom) { - - point1 = { - x: this.position.x, - y: this.position.y - }; - - point2 = { - x: this.position.x + this.width, - y: this.position.y - }; - - point3 = { - x: this.position.x + this.width, - y: this.position.y + this.height - }; - - point4 = { - x: this.position.x, - y: this.position.y + this.height - }; - - /* Now the transformations. */ - - if (this.parentFrame !== undefined) { // If there is not a parent then there is no point to check bounderies. - - point1 = this.parentFrame.frameThisPoint(point1); - point2 = this.parentFrame.frameThisPoint(point2); - point3 = this.parentFrame.frameThisPoint(point3); - point4 = this.parentFrame.frameThisPoint(point4); - - /* We add the actual displacement. */ - - point1 = this.container.displacement.displaceThisPoint(point1); - point2 = this.container.displacement.displaceThisPoint(point2); - point3 = this.container.displacement.displaceThisPoint(point3); - point4 = this.container.displacement.displaceThisPoint(point4); - - /* We temp zoom. */ - - point1 = tempZoom.zoomThisPoint(point1); - point2 = tempZoom.zoomThisPoint(point2); - point3 = tempZoom.zoomThisPoint(point3); - point4 = tempZoom.zoomThisPoint(point4); - - if (this.parentFrame.isThisPointHere(point1) === false) { - return false; - } - - if (this.parentFrame.isThisPointHere(point2) === false) { - return false; - } - - if (this.parentFrame.isThisPointHere(point3) === false) { - return false; - } - - if (this.parentFrame.isThisPointHere(point4) === false) { - return false; - } - - } - - return true; - } - - - function isThisPointHere(point, outsideViewPort) { // The second parameter is usefull when you want to check a point that you already know is outside the viewport. - - /* We need not to modify the point received, so me make a copy of it. */ - - var 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 (outsideViewPort === true) { - - checkPoint = this.container.displacement.undisplaceThisPoint(checkPoint); - checkPoint = this.container.frame.unframeThisPoint(checkPoint); - - } else { - checkPoint = unTransformThisPoint(checkPoint, frame.container); - } - - /* Now we check if the resulting point is whin the current Frame. */ - - if (checkPoint.x < 0 || checkPoint.y < 0 || checkPoint.x > frame.width || checkPoint.y > frame.height) { - - /* The point received is not in this frame */ - - return false; - - } else { - - /* The point is within this frame. */ - - return true; - - } - - } - - function draw(drawGrid, drawBorders, drawBackground) { - - - if (drawBorders === true) { - borders(); - } - - if (drawGrid === true) { - grid(); - } - - if (drawBackground === true) { - background(); - } - } - - function background() { - - - - borderPoint1 = { - x: PANEL_CORNERS_RADIOUS, - y: PANEL_CORNERS_RADIOUS - }; - - borderPoint2 = { - x: frame.width - PANEL_CORNERS_RADIOUS, - y: PANEL_CORNERS_RADIOUS - }; - - borderPoint3 = { - x: frame.width - PANEL_CORNERS_RADIOUS, - y: frame.height - PANEL_CORNERS_RADIOUS - }; - - borderPoint4 = { - x: PANEL_CORNERS_RADIOUS, - y: frame.height - PANEL_CORNERS_RADIOUS - }; - - titleBarPoint1 = { - x: 0, - y: TITLE_BAR_HEIGHT - }; - - titleBarPoint2 = { - x: frame.width, - y: TITLE_BAR_HEIGHT - }; - - - /* Now the transformations. */ - - borderPoint1 = frameThisPoint(borderPoint1); - borderPoint2 = frameThisPoint(borderPoint2); - borderPoint3 = frameThisPoint(borderPoint3); - borderPoint4 = frameThisPoint(borderPoint4); - - titleBarPoint1 = frameThisPoint(titleBarPoint1); - titleBarPoint2 = frameThisPoint(titleBarPoint2); - - /* We paint the panel background first */ - - browserCanvasContext.fillStyle = 'rgba(255, 255, 255, 0.75)'; - browserCanvasContext.beginPath(); - - browserCanvasContext.arc(borderPoint1.x, borderPoint1.y, PANEL_CORNERS_RADIOUS, 1.0 * Math.PI, 1.5 * Math.PI); - browserCanvasContext.lineTo(borderPoint2.x, borderPoint2.y - PANEL_CORNERS_RADIOUS); - browserCanvasContext.arc(borderPoint2.x, borderPoint2.y, PANEL_CORNERS_RADIOUS, 1.5 * Math.PI, 2.0 * Math.PI); - browserCanvasContext.lineTo(borderPoint3.x + PANEL_CORNERS_RADIOUS, borderPoint3.y); - browserCanvasContext.arc(borderPoint3.x, borderPoint3.y, PANEL_CORNERS_RADIOUS, 0 * Math.PI, 0.5 * Math.PI); - browserCanvasContext.lineTo(borderPoint4.x, borderPoint4.y + PANEL_CORNERS_RADIOUS); - browserCanvasContext.arc(borderPoint4.x, borderPoint4.y, PANEL_CORNERS_RADIOUS, 0.5 * Math.PI, 1.0 * Math.PI); - browserCanvasContext.lineTo(borderPoint1.x - PANEL_CORNERS_RADIOUS, borderPoint1.y); - - browserCanvasContext.closePath(); - - browserCanvasContext.fill(); - - browserCanvasContext.lineWidth = 0.1; - browserCanvasContext.strokeStyle = 'rgba(54, 54, 54, 0.75)'; - browserCanvasContext.stroke(); - - - - /* We paint the title bar now */ - - browserCanvasContext.fillStyle = 'rgba(37, 107, 219, 0.75)'; - browserCanvasContext.beginPath(); - - browserCanvasContext.moveTo(titleBarPoint1.x, titleBarPoint1.y); - browserCanvasContext.lineTo(borderPoint1.x - PANEL_CORNERS_RADIOUS, borderPoint1.y); - browserCanvasContext.arc(borderPoint1.x, borderPoint1.y, PANEL_CORNERS_RADIOUS, 1.0 * Math.PI, 1.5 * Math.PI); - browserCanvasContext.lineTo(borderPoint2.x, borderPoint2.y - PANEL_CORNERS_RADIOUS); - browserCanvasContext.arc(borderPoint2.x, borderPoint2.y, PANEL_CORNERS_RADIOUS, 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(12, 64, 148, 0.75)'; - browserCanvasContext.stroke(); - - /* print the title */ - - let labelPoint; - let fontSize = 10; - - browserCanvasContext.font = fontSize + 'px Courier New'; - - let label = frame.containerName; - - let xOffset = label.length / 2 * fontSize * FONT_ASPECT_RATIO; - let yOffset = (TITLE_BAR_HEIGHT - fontSize) / 2 + 2; - - labelPoint = { - x: frame.width / 2 - xOffset, - y: TITLE_BAR_HEIGHT - yOffset - }; - - labelPoint = frame.frameThisPoint(labelPoint); - - browserCanvasContext.fillStyle = 'rgba(240, 240, 240, 1)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - } - - - function borders() { - - - /* Lest get the important points of the drawing so as to apply the needed transformations. */ - - point1 = { - x: 0, - y: 0 - }; - - point2 = { - x: frame.width, - y: 0 - }; - - point3 = { - x: frame.width, - y: frame.height - }; - - point4 = { - x: 0, - y: frame.height - }; - - /* Now the transformations. */ - - point1 = transformThisPoint(point1, frame.container); - point2 = transformThisPoint(point2, frame.container); - point3 = transformThisPoint(point3, frame.container); - point4 = transformThisPoint(point4, frame.container); - - /* 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(150, 150, 150, 1)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - - - } - - - - function grid(smallLines) { - - if (smallLines === true) { - - /* Small Lines */ - - var step = frame.width / 100; - - browserCanvasContext.beginPath(); - - for (var i = 0; i < frame.width; i = i + step) { - - for (var j = 0; j < frame.height; j = j + step) { - - let point1 = { - x: 0, - y: j - }; - - let point2 = { - x: frame.width, - y: j - }; - - point1 = transformThisPoint(point1, frame.container); - point2 = transformThisPoint(point2, frame.container); - - browserCanvasContext.moveTo(point1.x, point1.y); - browserCanvasContext.lineTo(point2.x, point2.y); - } - - let point3 = { - x: frame.width, - y: frame.height - }; - - let point4 = { - x: 0, - y: frame.height - }; - - point3 = transformThisPoint(point3, frame.container); - point4 = transformThisPoint(point4, frame.container); - - 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.stroke(); - } - - - /* Main Lines */ - - var step = frame.width / 20; - - browserCanvasContext.beginPath(); - - for (var i = 0; i < frame.width; i = i + step) { - - for (var j = 0; j < frame.height; j = j + step) { - - let point1 = { - x: 0, - y: j - }; - - let point2 = { - x: frame.width, - y: j - }; - - point1 = transformThisPoint(point1, frame.container); - point2 = transformThisPoint(point2, frame.container); - - browserCanvasContext.moveTo(point1.x, point1.y); - browserCanvasContext.lineTo(point2.x, point2.y); - } - - let point3 = { - x: i, - y: 0 - }; - - let point4 = { - x: i, - y: frame.height - }; - - point3 = transformThisPoint(point3, frame.container); - point4 = transformThisPoint(point4, frame.container); - - 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.stroke(); - } -} \ No newline at end of file diff --git a/ImageButton.js b/ImageButton.js deleted file mode 100644 index 9a682942f..000000000 --- a/ImageButton.js +++ /dev/null @@ -1,127 +0,0 @@ -function newImageButton() { - - var thisObject = { - container: undefined, - draw: draw, - status: 'neutral', - type: undefined, - getContainer: getContainer, - initialize: initialize - }; - - var container = newContainer(); - container.initialize(); - container.isDraggeable = false; - container.isClickeable = true; - thisObject.container = container; - - let idNeutralImage; - let idOnImage; - let idOffImage; - let idDisabledImage; - - return thisObject; - - function initialize(pX, pY, pWidth, pHeight, pIdNeutralImage, pIdOnImage, pIdOffImage, pIdDisabledImage) { - - idNeutralImage = pIdNeutralImage; - idOnImage = pIdOnImage; - idOffImage = pIdOffImage; - idDisabledImage = pIdDisabledImage; - - /* Lets set the basic dimensions of this thisObject. */ - - var position = { - x: pX, - y: pY - }; - - thisObject.container.frame.position = position; - thisObject.container.frame.width = pWidth; - thisObject.container.frame.height = pHeight; - - /* Now we start listening to the onClick event from the mouse, so that we can allow users operate this thisObject. */ - - thisObject.container.eventHandler.listenToEvent('onMouseClick', buttonPressed, undefined); - - } - - function getContainer(point) { - - var container; - - /* First we check if this point is inside this space. */ - - if (this.container.frame.isThisPointHere(point, true) === true) { - - return this.container; - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - - } - - function buttonPressed(event, extraData) { - - switch (thisObject.status) { - - case 'neutral': - browserCanvasContext.fillStyle = onFillStyle; - return; - - case 'on': - thisObject.status = 'off'; - break; - - case 'off': - thisObject.status = 'on'; - break; - - case 'disabled': - return; - } - } - - function draw() { - - drawImage(); - - } - - function drawImage() { - - let image; - - let imagePoint = { - x: 0, - y: 0 - }; - - imagePoint = thisObject.container.frame.frameThisPoint(imagePoint); - - switch (thisObject.status) { - - case 'neutral': - image = document.getElementById(idNeutralImage); - break; - - case 'on': - image = document.getElementById(idOnImage); - break; - - case 'off': - image = document.getElementById(idOffImage); - break; - - case 'disabled': - image = document.getElementById(idDisabledImage); - break; - - } - browserCanvasContext.drawImage(image, imagePoint.x, imagePoint.y, thisObject.container.frame.width, thisObject.container.frame.height); - } -} \ No newline at end of file diff --git a/Images/AAMasters/AAMariam-Trading-Bot/Mariam.png b/Images/AAMasters/AAMariam-Trading-Bot/Mariam.png new file mode 100644 index 000000000..3e0e6cac2 Binary files /dev/null and b/Images/AAMasters/AAMariam-Trading-Bot/Mariam.png differ diff --git a/Images/AAMasters/AAMasters.png b/Images/AAMasters/AAMasters.png new file mode 100644 index 000000000..2949bda39 Binary files /dev/null and b/Images/AAMasters/AAMasters.png differ diff --git a/Images/AAMasters/AAOlivia-Indicator-Bot/Olivia.png b/Images/AAMasters/AAOlivia-Indicator-Bot/Olivia.png new file mode 100644 index 000000000..4a0f89279 Binary files /dev/null and b/Images/AAMasters/AAOlivia-Indicator-Bot/Olivia.png differ diff --git a/Images/AAMasters/AARobert-Trading-Bot/Robert.png b/Images/AAMasters/AARobert-Trading-Bot/Robert.png new file mode 100644 index 000000000..c863ad3db Binary files /dev/null and b/Images/AAMasters/AARobert-Trading-Bot/Robert.png differ diff --git a/Images/AAMasters/AATom-Indicator-Bot/Tom.png b/Images/AAMasters/AATom-Indicator-Bot/Tom.png new file mode 100644 index 000000000..dfd356173 Binary files /dev/null and b/Images/AAMasters/AATom-Indicator-Bot/Tom.png differ diff --git a/Images/AAMasters/PlottersCandlesVolumes/Candles.png b/Images/AAMasters/PlottersCandlesVolumes/Candles.png new file mode 100644 index 000000000..f1e093cd5 Binary files /dev/null and b/Images/AAMasters/PlottersCandlesVolumes/Candles.png differ diff --git a/Images/AAMasters/PlottersCandlesVolumes/Volumes.png b/Images/AAMasters/PlottersCandlesVolumes/Volumes.png new file mode 100644 index 000000000..aaea183a6 Binary files /dev/null and b/Images/AAMasters/PlottersCandlesVolumes/Volumes.png differ diff --git a/Images/AAMasters/PlottersStairsPatterns/CandleStairs.png b/Images/AAMasters/PlottersStairsPatterns/CandleStairs.png new file mode 100644 index 000000000..a21beb7a0 Binary files /dev/null and b/Images/AAMasters/PlottersStairsPatterns/CandleStairs.png differ diff --git a/Images/AAMasters/PlottersStairsPatterns/VolumeStairs.png b/Images/AAMasters/PlottersStairsPatterns/VolumeStairs.png new file mode 100644 index 000000000..363096eee Binary files /dev/null and b/Images/AAMasters/PlottersStairsPatterns/VolumeStairs.png differ diff --git a/Images/AAMasters/PlottersTrading/Competition.png b/Images/AAMasters/PlottersTrading/Competition.png new file mode 100644 index 000000000..a4ee16263 Binary files /dev/null and b/Images/AAMasters/PlottersTrading/Competition.png differ diff --git a/Images/AAMasters/PlottersTrading/History.png b/Images/AAMasters/PlottersTrading/History.png new file mode 100644 index 000000000..a6809adfd Binary files /dev/null and b/Images/AAMasters/PlottersTrading/History.png differ diff --git a/Images/AAMasters/PlottersTrading/Live.png b/Images/AAMasters/PlottersTrading/Live.png new file mode 100644 index 000000000..71f565286 Binary files /dev/null and b/Images/AAMasters/PlottersTrading/Live.png differ diff --git a/Images/AASpartans/AAKing-Trading-Bot/King.png b/Images/AASpartans/AAKing-Trading-Bot/King.png new file mode 100644 index 000000000..c9622fcc1 Binary files /dev/null and b/Images/AASpartans/AAKing-Trading-Bot/King.png differ diff --git a/Images/AASpartans/AASpartans.png b/Images/AASpartans/AASpartans.png new file mode 100644 index 000000000..d1c7106fa Binary files /dev/null and b/Images/AASpartans/AASpartans.png differ diff --git a/Images/AAVikings/AAArtudito-Trading-Bot/Artudito.png b/Images/AAVikings/AAArtudito-Trading-Bot/Artudito.png new file mode 100644 index 000000000..a32ade82a Binary files /dev/null and b/Images/AAVikings/AAArtudito-Trading-Bot/Artudito.png differ diff --git a/Images/AAVikings/AAGauss-Indicator-Bot/Gauss.png b/Images/AAVikings/AAGauss-Indicator-Bot/Gauss.png new file mode 100644 index 000000000..b0c7df225 Binary files /dev/null and b/Images/AAVikings/AAGauss-Indicator-Bot/Gauss.png differ diff --git a/Images/AAVikings/AAVikings.png b/Images/AAVikings/AAVikings.png new file mode 100644 index 000000000..315b1b4f1 Binary files /dev/null and b/Images/AAVikings/AAVikings.png differ diff --git a/Images/AAVikings/PlotterLinearRegressionCurve/LRC.png b/Images/AAVikings/PlotterLinearRegressionCurve/LRC.png new file mode 100644 index 000000000..a2cefe275 Binary files /dev/null and b/Images/AAVikings/PlotterLinearRegressionCurve/LRC.png differ diff --git a/Images/Algoteers/Algoteers.png b/Images/Algoteers/Algoteers.png new file mode 100644 index 000000000..39529f0c2 Binary files /dev/null and b/Images/Algoteers/Algoteers.png differ diff --git a/Images/Algoteers/Mousequeton-Trading-Bot/Mousequeton.png b/Images/Algoteers/Mousequeton-Trading-Bot/Mousequeton.png new file mode 100644 index 000000000..1592c4d9f Binary files /dev/null and b/Images/Algoteers/Mousequeton-Trading-Bot/Mousequeton.png differ diff --git a/Images/CryptoRockets/CryptoRockets.png b/Images/CryptoRockets/CryptoRockets.png new file mode 100644 index 000000000..f5977429c Binary files /dev/null and b/Images/CryptoRockets/CryptoRockets.png differ diff --git a/Images/CryptoRockets/RocketFeller-Trading-Bot/rocketfeller.png b/Images/CryptoRockets/RocketFeller-Trading-Bot/rocketfeller.png new file mode 100644 index 000000000..c2151550e Binary files /dev/null and b/Images/CryptoRockets/RocketFeller-Trading-Bot/rocketfeller.png differ diff --git a/Images/DummyTeam/DummyBot-Trading-Bot/DummyBot.png b/Images/DummyTeam/DummyBot-Trading-Bot/DummyBot.png new file mode 100644 index 000000000..ff37c3d91 Binary files /dev/null and b/Images/DummyTeam/DummyBot-Trading-Bot/DummyBot.png differ diff --git a/Images/DummyTeam/DummyTeam.png b/Images/DummyTeam/DummyTeam.png new file mode 100644 index 000000000..1e5f3e9fc Binary files /dev/null and b/Images/DummyTeam/DummyTeam.png differ diff --git a/Images/Icons/chart-scale.png b/Images/Icons/chart-scale.png new file mode 100644 index 000000000..bcffba53f Binary files /dev/null and b/Images/Icons/chart-scale.png differ diff --git a/Images/Icons/pause.png b/Images/Icons/pause.png new file mode 100644 index 000000000..2cfc0ff09 Binary files /dev/null and b/Images/Icons/pause.png differ diff --git a/Images/Icons/play.png b/Images/Icons/play.png new file mode 100644 index 000000000..81862cec5 Binary files /dev/null and b/Images/Icons/play.png differ diff --git a/Images/Icons/trash.png b/Images/Icons/trash.png new file mode 100644 index 000000000..427c65d5d Binary files /dev/null and b/Images/Icons/trash.png differ diff --git a/Images/KnightsTemplar/Inquisitor-Trading-Bot/Inquisitor.png b/Images/KnightsTemplar/Inquisitor-Trading-Bot/Inquisitor.png new file mode 100644 index 000000000..2fd9598e3 Binary files /dev/null and b/Images/KnightsTemplar/Inquisitor-Trading-Bot/Inquisitor.png differ diff --git a/Images/KnightsTemplar/KnightsTemplar.png b/Images/KnightsTemplar/KnightsTemplar.png new file mode 100644 index 000000000..77cd3ef80 Binary files /dev/null and b/Images/KnightsTemplar/KnightsTemplar.png differ diff --git a/Images/PotterHeads/Nimbus2000-Trading-Bot/Nimbus2000.png b/Images/PotterHeads/Nimbus2000-Trading-Bot/Nimbus2000.png new file mode 100644 index 000000000..c2dcf5b31 Binary files /dev/null and b/Images/PotterHeads/Nimbus2000-Trading-Bot/Nimbus2000.png differ diff --git a/Images/PotterHeads/PotterHeads.png b/Images/PotterHeads/PotterHeads.png new file mode 100644 index 000000000..ab8a6183c Binary files /dev/null and b/Images/PotterHeads/PotterHeads.png differ diff --git a/Images/Titanomachy/Atlas-Trading-Bot/Atlas.png b/Images/Titanomachy/Atlas-Trading-Bot/Atlas.png new file mode 100644 index 000000000..91d7d8d2f Binary files /dev/null and b/Images/Titanomachy/Atlas-Trading-Bot/Atlas.png differ diff --git a/Images/Titanomachy/Titanomachy.png b/Images/Titanomachy/Titanomachy.png new file mode 100644 index 000000000..fc03818f3 Binary files /dev/null and b/Images/Titanomachy/Titanomachy.png differ diff --git a/Images/aa-logo-background.png b/Images/aa-logo-background.png new file mode 100644 index 000000000..cb84e7a21 Binary files /dev/null and b/Images/aa-logo-background.png differ diff --git a/Images/aa-logo-dark-8.png b/Images/aa-logo-dark-8.png new file mode 100644 index 000000000..a6bcfcb90 Binary files /dev/null and b/Images/aa-logo-dark-8.png differ diff --git a/Images/aa-logo-vert.png b/Images/aa-logo-vert.png new file mode 100644 index 000000000..c866edb8f Binary files /dev/null and b/Images/aa-logo-vert.png differ diff --git a/Images/aa-logo.png b/Images/aa-logo.png new file mode 100644 index 000000000..9701ee1c0 Binary files /dev/null and b/Images/aa-logo.png differ diff --git a/Images/bitcoin-logo-background.png b/Images/bitcoin-logo-background.png new file mode 100644 index 000000000..1627225c4 Binary files /dev/null and b/Images/bitcoin-logo-background.png differ diff --git a/Images/bitcoin-logo.png b/Images/bitcoin-logo.png new file mode 100644 index 000000000..17f314ce3 Binary files /dev/null and b/Images/bitcoin-logo.png differ diff --git a/Images/favicon.ico b/Images/favicon.ico new file mode 100644 index 000000000..cd2c5d6af Binary files /dev/null and b/Images/favicon.ico differ diff --git a/Images/menu.icon.off.1.gif b/Images/menu.icon.off.1.gif new file mode 100644 index 000000000..163aa9533 Binary files /dev/null and b/Images/menu.icon.off.1.gif differ diff --git a/Images/menu.icon.off.2.gif b/Images/menu.icon.off.2.gif new file mode 100644 index 000000000..e157299e2 Binary files /dev/null and b/Images/menu.icon.off.2.gif differ diff --git a/Images/menu.icon.off.3.gif b/Images/menu.icon.off.3.gif new file mode 100644 index 000000000..f8879f485 Binary files /dev/null and b/Images/menu.icon.off.3.gif differ diff --git a/Images/menu.icon.off.4.gif b/Images/menu.icon.off.4.gif new file mode 100644 index 000000000..c5934962c Binary files /dev/null and b/Images/menu.icon.off.4.gif differ diff --git a/Images/menu.icon.on.1.gif b/Images/menu.icon.on.1.gif new file mode 100644 index 000000000..3f840dee1 Binary files /dev/null and b/Images/menu.icon.on.1.gif differ diff --git a/Images/menu.icon.on.2.gif b/Images/menu.icon.on.2.gif new file mode 100644 index 000000000..85580ee33 Binary files /dev/null and b/Images/menu.icon.on.2.gif differ diff --git a/Images/menu.icon.on.3.gif b/Images/menu.icon.on.3.gif new file mode 100644 index 000000000..3e79c9081 Binary files /dev/null and b/Images/menu.icon.on.3.gif differ diff --git a/Images/menu.icon.on.4.gif b/Images/menu.icon.on.4.gif new file mode 100644 index 000000000..031f9b5c5 Binary files /dev/null and b/Images/menu.icon.on.4.gif differ diff --git a/Images/poloniex-logo-background.png b/Images/poloniex-logo-background.png new file mode 100644 index 000000000..b589e405f Binary files /dev/null and b/Images/poloniex-logo-background.png differ diff --git a/Images/poloniex-logo.png b/Images/poloniex-logo.png new file mode 100644 index 000000000..798c46a19 Binary files /dev/null and b/Images/poloniex-logo.png differ diff --git a/Images/tether-logo-background.png b/Images/tether-logo-background.png new file mode 100644 index 000000000..db90501a4 Binary files /dev/null and b/Images/tether-logo-background.png differ diff --git a/Images/tether-logo.png b/Images/tether-logo.png new file mode 100644 index 000000000..7f8fc88f7 Binary files /dev/null and b/Images/tether-logo.png differ diff --git a/Markets.js b/Markets.js deleted file mode 100644 index eb6d43d76..000000000 --- a/Markets.js +++ /dev/null @@ -1,164 +0,0 @@ -/* This business logic module is responsible for Markets and Assets. */ - -const MODULE_NAME = "Markets"; -var DATA_LAYER_MODULE = require('./Data Layer'); - - -exports.newMarkets = function newMarkets() { - - markets = { - getMarketsByExchange: getMarketsByExchange, - getExistingOrNewMarketId: getExistingOrNewMarketId, - createNewAsset: createNewAsset, - createNewMarket: createNewMarket, - initialize: initialize - }; - - var dataLayer; - - return markets; - - - - function initialize() { - - dataLayer = DATA_LAYER_MODULE.newDataLayer(); - - } - - - - function getMarketsByExchange(exchangeId, callBackFunction) { - - dataLayer.getMarketsByExchange(exchangeId, onResultSetReady); - - function onResultSetReady(resultSet) { - - callBackFunction(resultSet); - - } - - } - - - function getExistingOrNewMarketId(marketName, market, exchangeId, callBackFunction) { - - var assets = marketName.split("_"); - - var assetCodeA = assets[0]; - var assetCodeB = assets[1]; - - var idAssetA; - var idAssetB; - - var marketId; - - getIdAssetA(); - - function getIdAssetA() { - - dataLayer.getAssetIdByCodeName(assetCodeA, onIdReady); - - function onIdReady(resultSet) { - - idAssetA = resultSet.getValue(1, "Id"); - - if (idAssetA === undefined) { - - createNewAsset(assetCodeA, onAssetCreated); - - function onAssetCreated(resultSet) { - - idAssetA = resultSet.getValue(1, "Id"); - getIdAssetB(); - } - - } else { - getIdAssetB(); - } - } - } - - function getIdAssetB() { - - dataLayer.getAssetIdByCodeName(assetCodeB, onIdReady); - - function onIdReady(resultSet) { - - idAssetB = resultSet.getValue(1, "Id"); - - if (idAssetB === undefined) { - - createNewAsset(assetCodeB, onAssetCreated); - - function onAssetCreated(resultSet) { - - idAssetB = resultSet.getValue(1, "Id"); - getMarketId(); - } - - } else { - getMarketId(); - } - } - } - - function getMarketId() { - - dataLayer.getMarketId(idAssetA, idAssetB, exchangeId, onIdReady); - - function onIdReady(resultSet) { - - marketId = resultSet.getValue(1, "Id"); - - if (marketId === undefined) { - - createNewMarket(exchangeId, idAssetA, idAssetB, onMarketCreated); - - function onMarketCreated(resultSet) { - - marketId = resultSet.getValue(1, "Id"); - respond(); - } - - } else { - respond(); - } - } - } - - function respond() { - - callBackFunction(marketName, market, marketId); - - } - } - - - function createNewAsset(assetCode, callBackFunction) { - - dataLayer.newAsset(assetCode, onIdReady); - - function onIdReady(resultSet) { - - callBackFunction(resultSet); - - } - - } - - - - function createNewMarket(exchangeId, idAssetA, idAssetB, callBackFunction) { - - dataLayer.newMarket(exchangeId, idAssetA, idAssetB, onIdReady); - - function onIdReady(resultSet) { - - callBackFunction(resultSet); - - } - - } - -}; \ No newline at end of file diff --git a/Panels/ProductsPanel.js b/Panels/ProductsPanel.js deleted file mode 100644 index e02f31910..000000000 --- a/Panels/ProductsPanel.js +++ /dev/null @@ -1,236 +0,0 @@ -function newProductsPanel() { - - var thisObject = { - container: undefined, - getLoadingProductCards: getLoadingProductCards, - draw: draw, - getContainer: getContainer, // returns the inner most container that holds the point received by parameter. - initialize: initialize - }; - - /* Cointainer stuff */ - - var container = newContainer(); - - container.name = "Algobots Product Panel"; - container.initialize(); - - container.isDraggeable = true; - container.isWheelable = true; - - thisObject.container = container; - thisObject.container.frame.containerName = "Algobots's Products"; - - let isInitialized = false; - let productCards = []; - - let visibleProductCards = []; - let firstVisibleCard = 1; - - return thisObject; - - function initialize() { - - thisObject.container.frame.width = 350; - thisObject.container.frame.height = viewPort.visibleArea.bottomRight.y - viewPort.visibleArea.topLeft.y + 15; - - var position = { - x: viewPort.visibleArea.topLeft.x, - y: 0 - }; - - thisObject.container.frame.position = position; - - /* Needed Variables */ - - var lastY = 5; - - /* First thing is to build the productCards array */ - - let devTeams = ecosystem.getTeams(); - - for (let i = 0; i < devTeams.length; i++) { - - let devTeam = devTeams[i]; - - for (let j = 0; j < devTeam.bots.length; j++) { - - let bot = devTeam.bots[j]; - - for (let k = 0; k < bot.products.length; k++) { - - let product = bot.products[k]; - - /* Now we create Product objects */ - - let productCard = newProductCard(); - - productCard.devTeam = devTeam; - productCard.bot = bot; - productCard.product = product; - - productCard.code = devTeam.codeName + '-' + bot.codeName + '-' + product.codeName; - - /* Initialize it */ - - productCard.initialize(); - - /* Container Stuff */ - - productCard.container.displacement.parentDisplacement = thisObject.container.displacement; - productCard.container.frame.parentFrame = thisObject.container.frame; - productCard.container.parentContainer = thisObject.container; - productCard.container.isWheelable = true; - - /* Positioning within thisObject Panel */ - - let position = { - x: 10, - y: thisObject.container.frame.height - thisObject.container.frame.getBodyHeight() - }; - productCard.container.frame.position.x = position.x; - productCard.container.frame.position.y = position.y + lastY; - - lastY = lastY + productCard.container.frame.height; - - /* Add to Visible Product Array */ - - if (lastY < thisObject.container.frame.height) { - - visibleProductCards.push(productCard); - - } - - /* Add to the Product Array */ - - productCards.push(productCard); - - /* Listen to Status Changes Events */ - - productCard.container.eventHandler.listenToEvent('Status Changed', onProductCardStatusChanged); - productCard.container.eventHandler.listenToEvent('Mouse Wheel', onMouseWheel); - - } - } - } - - thisObject.container.eventHandler.listenToEvent("Mouse Wheel", onMouseWheel); - isInitialized = true; - - } - - function onMouseWheel(pDelta) { - - if (pDelta > 0) { - pDelta = 1; - } else { - pDelta = -1; - } - - firstVisibleCard = firstVisibleCard + pDelta; - - let availableSlots = visibleProductCards.length; - - if (firstVisibleCard < 1) { firstVisibleCard = 1; } - if (firstVisibleCard > (productCards.length - availableSlots + 1)) { firstVisibleCard = productCards.length - availableSlots + 1; } - - visibleProductCards = []; - var lastY = 5; - - for (let i = 0; i < productCards.length; i++) { - - if (i + 1 >= firstVisibleCard && i + 1 < firstVisibleCard + availableSlots) { - - let productCard = productCards[i]; - - /* Positioning within thisObject Panel */ - - let position = { - x: 10, - y: thisObject.container.frame.height - thisObject.container.frame.getBodyHeight() - }; - productCard.container.frame.position.x = position.x; - productCard.container.frame.position.y = position.y + lastY; - - lastY = lastY + productCard.container.frame.height; - - /* Add to Visible Product Array */ - - visibleProductCards.push(productCard); - } - } - } - - function onProductCardStatusChanged(pProductCard) { - - thisObject.container.eventHandler.raiseEvent('Product Card Status Changed', pProductCard); - - } - - function getLoadingProductCards() { - - /* Returns all productCards which status is LOADING */ - - let onProducts = []; - - for (let i = 0; i < productCards.length; i++) { - - if (productCards[i].status === PRODUCT_CARD_STATUS.LOADING) { - - onProducts.push(productCards[i]); - } - } - - return onProducts; - } - - function getContainer(point) { - - var 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 (var i = 0; i < visibleProductCards.length; i++) { - - container = visibleProductCards[i].getContainer(point); - - if (container !== undefined) { - - /* We found an inner container which has the point. We return it. */ - - return container; - } - } - - /* 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 thisObject space. */ - - return undefined; - } - - } - - - function draw() { - - if (isInitialized === false) {return;} - - thisObject.container.frame.draw(false, false, true); - - for (let i = 0; i < visibleProductCards.length; i++) { - visibleProductCards[i].draw(); - } - - } - -} \ No newline at end of file diff --git a/Panels/TimeControlPanel.js b/Panels/TimeControlPanel.js deleted file mode 100644 index 949cb69af..000000000 --- a/Panels/TimeControlPanel.js +++ /dev/null @@ -1,361 +0,0 @@ - - - -function newTimeControlPanel() { - - let PLAY_STEP = 0; - - var thisObject = { - container: undefined, - buttons: undefined, - setDatetime: setDatetime, - datetimeDisplay: undefined, - stepDisplay: undefined, - draw: draw, - getContainer: getContainer, // returns the inner most container that holds the point received by parameter. - initialize: initialize - }; - - var container = newContainer(); - container.initialize(); - container.isDraggeable = true; - thisObject.container = container; - thisObject.container.frame.containerName = "Time Control Panel"; - - var playingLoop; // Controls the generation of events that allows to play continuosly the market. - - return thisObject; - - - - - function initialize() { - - this.container.frame.width = 200; - this.container.frame.height = 85; - - var position = { - x: (viewPort.visibleArea.topRight.x - viewPort.visibleArea.topLeft.x) / 2 - this.container.frame.width / 2, - y: 0 - }; - - this.container.frame.position = position; - - var buttonPosition; - - var buttonNames = ['Fast Backwards', 'Play Backwards', 'Step Backwards', 'Pause', 'Step Forward', 'Play Forward', 'Fast Forward']; - var lastX = 0; - var buttons = []; - - for (var i = 0; i < buttonNames.length; i++) { - - /* Buttons are going to be one at the right of the other. */ - - var button = newButton(); - button.type = buttonNames[i]; - - button.container.displacement.parentDisplacement = this.container.displacement; - button.container.frame.parentFrame = this.container.frame; - - button.container.parentContainer = this.container; - - button.initialize(); - - buttonPosition = { // The first button - x: thisObject.container.frame.width / 2 - (buttonNames.length * button.container.frame.width) / 2, - y: thisObject.container.frame.height * 7 / 10 - }; - button.container.frame.position.x = buttonPosition.x + lastX; - button.container.frame.position.y = buttonPosition.y; - - lastX = lastX + button.container.frame.width; - - /* We start listening to the buttons click event, so as to know when one was pressed. */ - - button.container.eventHandler.listenToEvent('onMouseClick', buttonPressed, i); - - if (buttonNames[i] === 'Pause') { - button.status = 'on'; // We turn on by default the PAUSE button. - } - - buttons.push(button); - - } - - - - thisObject.buttons = buttons; - - - var datetime = INITIAL_DATE; - - var datetimeDisplay = { - currentDatetime: datetime, - addTime: addTime, - draw: drawTimeDisplay - }; - - thisObject.buttons = buttons; - thisObject.datetimeDisplay = datetimeDisplay; - - - viewPort.eventHandler.listenToEvent("Zoom Changed", onZoomChanged); - PLAY_STEP = INITIAL_TIME_PERIOD; - - } - - - function getContainer(point) { - - var container; - - /* First we check if this point is inside this space. */ - - if (this.container.frame.isThisPointHere(point, true) === true) { - - /* Now we see which is the inner most container that has it */ - - - for (var i = 0; i < this.buttons.length; i++) { - - container = this.buttons[i].getContainer(point); - - if (container !== undefined) { - - /* We found an inner container which has the point. We return it. */ - - return container; - } - } - - /* The point does not belong to any inner container, so we return the current container. */ - - return this.container; - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - - } - - - function onZoomChanged(event) { - - PLAY_STEP = recalculatePeriod(event.newLevel); - - } - - - function setDatetime(newDatetime) { - - thisObject.datetimeDisplay.currentDatetime = newDatetime; - - } - - - - function addTime(seconds) { - - var newDate = thisObject.datetimeDisplay.currentDatetime; - - newDate.setSeconds(newDate.getSeconds() + seconds); - thisObject.datetimeDisplay.currentDatetime = newDate; - - } - - function buttonPressed(event, buttonPressedIndex) { - - clearInterval(playingLoop); - - /* When any of the buttons are pressed, the control panel must turn off the resto of the buttons. */ - - for (var i = 0; i < thisObject.buttons.length; i++) { - - if (i !== buttonPressedIndex) { - - var button = thisObject.buttons[i]; - - button.status = 'off'; - - } - - } - - /* Now we actualy do something depending on which button was pressed. */ - - switch (thisObject.buttons[buttonPressedIndex].type) { - - case "Fast Backwards": - - pressFastBackwards(); - break; - - case "Play Backwards": - - pressPlayBackwards(); - break; - - case "Step Backwards": - - pressStepBackwards(); - break; - - case "Pause": - - pressPause(); - break; - - case "Step Forward": - - pressStepForward(); - break; - - case "Play Forward": - - pressPlayForward(); - break; - - case "Fast Forward": - - pressFastForward(); - break; - - default: - - - } - - } - - function pressFastBackwards() { - - clearInterval(playingLoop); - playingLoop = setInterval(pressStepBackwards, 100); - - } - - function pressPlayBackwards() { - - clearInterval(playingLoop); - playingLoop = setInterval(pressStepBackwards, 1000); - - } - - function pressStepBackwards() { - - thisObject.datetimeDisplay.addTime(-1 * PLAY_STEP / 1000); - - var newDatetime = thisObject.datetimeDisplay.currentDatetime; - - thisObject.container.eventHandler.raiseEvent('Datetime Changed', newDatetime); - - } - - function pressPause() { - clearInterval(playingLoop); - } - - function pressStepForward() { - - thisObject.datetimeDisplay.addTime(PLAY_STEP / 1000); - - var newDatetime = thisObject.datetimeDisplay.currentDatetime; - - thisObject.container.eventHandler.raiseEvent('Datetime Changed', newDatetime); - - } - - function pressPlayForward() { - - clearInterval(playingLoop); - playingLoop = setInterval(pressStepForward, 1000); - - } - - function pressFastForward() { - - playingLoop = setInterval(pressStepForward, 100); - - } - - - - function draw() { - - thisObject.container.frame.draw(false, false, true); - - for (var i = 0; i < thisObject.buttons.length; i++) { - thisObject.buttons[i].draw(); - } - - this.datetimeDisplay.draw(); - - } - - - - - function drawTimeDisplay() { - - let fontSize = 10; - - let label = thisObject.datetimeDisplay.currentDatetime.toUTCString(); - let labelArray = label.split(" "); - - label = labelArray[0] + " " + labelArray[1] + " " + labelArray[2] + " " + labelArray[3]; - - /* Now we transform x on the actual coordinate on the canvas. */ - - let labelPoint = { - x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO, - y: thisObject.container.frame.height * 3.4 / 10 - }; - - labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - /* The time */ - - label = labelArray[4] + " " + labelArray[5]; - - /* Now we transform x on the actual coordinate on the canvas. */ - - labelPoint = { - x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO, - y: thisObject.container.frame.height * 5.0 / 10 - }; - - labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - label = PLAY_STEP / _1_MINUTE_IN_MILISECONDS + ' min'; - - /* Now we transform x on the actual coordinate on the canvas. */ - - labelPoint = { - x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO, - y: thisObject.container.frame.height * 6.5 / 10 - }; - - labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - } - - - - - -} \ No newline at end of file diff --git a/ProductCard.js b/ProductCard.js deleted file mode 100644 index b4de94894..000000000 --- a/ProductCard.js +++ /dev/null @@ -1,762 +0,0 @@ -function newProductCard() { - - const CONSOLE_LOG = false; - - var thisObject = { - container: undefined, - draw: draw, - status: 'off', - devTeam: undefined, - bot: undefined, - product: undefined, - code: undefined, - - setDatetime: setDatetime, - setTimePeriod: setTimePeriod, - - onMarketFileLoaded: onMarketFileLoaded, - onDailyFileLoaded: onDailyFileLoaded, - onSingleFileLoaded: onSingleFileLoaded, - onFileSequenceLoaded: onFileSequenceLoaded, - - getContainer: getContainer, // returns the inner most container that holds the point received by parameter. - initialize: initialize - }; - - 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(150, 150, 150, @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 timePeriod = INITIAL_TIME_PERIOD; - let datetime = INITIAL_DATE; - - return thisObject; - - function initialize() { - - /* Create this objects continer */ - - var container = newContainer(); - container.name = "Product Card " + thisObject.code; - container.initialize(); - container.isDraggeable = false; - container.isClickeable = true; - thisObject.container = container; - - /* Add information that will later be needed.*/ - - let team = ecosystem.getTeam(thisObject.product.plotter.devTeam); - let plotter = ecosystem.getPlotter(team, thisObject.product.plotter.codeName); - let plotterModule = ecosystem.getPlotterModule(plotter, thisObject.product.plotter.moduleName); - - thisObject.product.plotter.profilePicture = plotterModule.profilePicture; - thisObject.product.plotter.module = plotterModule; - - /* Lets set the basic dimensions of this thisObject. */ - - var position = { - x: 0, - y: 0 - }; - - this.container.frame.position = position; - this.container.frame.width = 330; - this.container.frame.height = 100; - - /* We retrieve the locally stored status of the Product */ - - let storedValue = window.localStorage.getItem(thisObject.code); - - if (storedValue !== null) { - - thisObject.status = storedValue; - - if (thisObject.status === PRODUCT_CARD_STATUS.ON) { - - changeStatusTo(PRODUCT_CARD_STATUS.LOADING); - - } - - } else { - - changeStatusTo(PRODUCT_CARD_STATUS.LOADING); // This happens the first time the app is run on a new browser. - - } - - /* Lets listen to our own events to react when we have a Mouse Click */ - - thisObject.container.eventHandler.listenToEvent('onMouseClick', buttonPressed); - - } - - function getContainer(point) { - - var container; - - /* First we check if this point is inside this space. */ - - if (this.container.frame.isThisPointHere(point, true) === true) { - - return this.container; - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - - } - - 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 (timePeriod <= _1_HOUR_IN_MILISECONDS) { - - dailyFileProgressBar.animatedValue = 0; - - if (CONSOLE_LOG === true) { - - console.log("ProductCard -> onDayChanged -> dailyFileProgressBar.animatedValue = " + dailyFileProgressBar.animatedValue); - - } - } - } - } - - function setTimePeriod(pTimePeriod) { - - /* - - 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 (timePeriod !== pTimePeriod) { - - timePeriod = pTimePeriod; - - if (timePeriod <= _1_HOUR_IN_MILISECONDS) { - - dailyFileProgressBar.animatedValue = 0; - - if (CONSOLE_LOG === true) { - - console.log("ProductCard -> onTimePeriodChanged -> dailyFileProgressBar.animatedValue = " + dailyFileProgressBar.animatedValue); - - } - } - } - } - - function onMarketFileLoaded(event) { - - marketFileProgressBar.value = Math.trunc(event.currentValue * 100 / event.totalValue); - marketFileProgressBar.fillStyle = LOADING_FILL_STYLE; - marketFileProgressBar.strokeStyle = LOADING_STROKE_STYLE; - - if (marketFileProgressBar.value > 100) { marketFileProgressBar.value = 100; } - - if (CONSOLE_LOG === true) { - - console.log("ProductCard onMarketFileLoaded Value = " + marketFileProgressBar.value + "% for " + thisObject.code + ". Event = " + JSON.stringify(event)); - - } - } - - 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;} - - if (CONSOLE_LOG === true) { - - console.log("ProductCard onDailyFileLoaded Value = " + dailyFileProgressBar.value + "% for " + thisObject.code + ". Event = " + JSON.stringify(event)); - - } - } - - 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; } - - if (CONSOLE_LOG === true) { - - console.log("ProductCard onSingleFileLoaded Value = " + singleFileProgressBar.value + "% for " + thisObject.code + ". Event = " + JSON.stringify(event)); - - } - } - - 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; } - - if (CONSOLE_LOG === true) { - - console.log("ProductCard onFileSequenceLoaded Value = " + fileSequenceProgressBar.value + "% for " + thisObject.code + ". Event = " + JSON.stringify(event)); - - } - } - - function buttonPressed(event) { - - switch (thisObject.status) { - - case PRODUCT_CARD_STATUS.ON: - - changeStatusTo(PRODUCT_CARD_STATUS.OFF); - - 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; - - break; - - case PRODUCT_CARD_STATUS.OFF: - - changeStatusTo(PRODUCT_CARD_STATUS.LOADING); - - break; - - } - } - - function changeStatusTo(pNewStatus) { - - if (thisObject.status !== pNewStatus) { - - thisObject.status = pNewStatus; - - let eventData = thisObject; - - thisObject.container.eventHandler.raiseEvent('Status Changed', eventData); - - window.localStorage.setItem(thisObject.code, thisObject.status); - - } - } - - function draw() { - - drawProductCard(); - - } - - function drawProductCard() { - - /* - - Put images on the card. - - */ - - const devTeamImageSize = 50; - const botImageSize = 50; - const plotterImageSize = { - width: 180, - height: 50 - }; - - /* First the Dev Team Profile Picture. */ - - let teamImagePoint = { - x: 10, - y: thisObject.container.frame.height / 2 - devTeamImageSize / 2 - }; - - teamImagePoint = thisObject.container.frame.frameThisPoint(teamImagePoint); - - let teamImage = document.getElementById(thisObject.bot.devTeam + ".png"); - browserCanvasContext.drawImage(teamImage, teamImagePoint.x, teamImagePoint.y, devTeamImageSize, devTeamImageSize); - - /* Second the Bot's Profile Picture. */ - - if (thisObject.bot.profilePicture !== undefined) { - - let botImagePoint = { - x: thisObject.container.frame.width - 10 - botImageSize, - y: thisObject.container.frame.height / 2 - botImageSize / 2 - }; - - botImagePoint = thisObject.container.frame.frameThisPoint(botImagePoint); - - let imageId = thisObject.bot.devTeam + "." + thisObject.bot.profilePicture; - let botImage = document.getElementById(imageId); - browserCanvasContext.drawImage(botImage, botImagePoint.x, botImagePoint.y, botImageSize, botImageSize); - - } - - /* Third the Plotter's Profile Picture. */ - - if (thisObject.product.plotter.profilePicture !== undefined) { - - let plotterImagePoint = { - x: thisObject.container.frame.width / 2 - plotterImageSize.width / 2, - y: thisObject.container.frame.height / 2 - plotterImageSize.height / 2 - }; - - plotterImagePoint = thisObject.container.frame.frameThisPoint(plotterImagePoint); - - let imageId = thisObject.product.plotter.devTeam + "." + thisObject.product.plotter.codeName + "." + thisObject.product.plotter.moduleName + "." + thisObject.product.plotter.profilePicture; - let plotterImage = document.getElementById(imageId); - browserCanvasContext.drawImage(plotterImage, plotterImagePoint.x, plotterImagePoint.y, plotterImageSize.width, plotterImageSize.height); - - } - - - /* Now the small circle */ - - centerPoint = { - x: thisObject.container.frame.width / 2, - y: thisObject.container.frame.height / 2 - }; - - /* Now the transformations. */ - - centerPoint = thisObject.container.frame.frameThisPoint(centerPoint); - - /* Lets start the drawing. */ - - browserCanvasContext.beginPath(); - browserCanvasContext.arc(centerPoint.x, centerPoint.y, 3, 0, 2 * Math.PI); - browserCanvasContext.closePath(); - - let offFillStyle = 'rgba(232, 28, 28, 0.75)'; - let onFillStyle = 'rgba(45, 232, 28, 0.75)'; - let loadingFillStyle = 'rgba(237, 227, 47, 0.75)'; - - switch (thisObject.status) { - - case PRODUCT_CARD_STATUS.ON: - browserCanvasContext.fillStyle = onFillStyle; - break; - - case PRODUCT_CARD_STATUS.OFF: - browserCanvasContext.fillStyle = offFillStyle; - break; - - case PRODUCT_CARD_STATUS.LOADING: - browserCanvasContext.fillStyle = loadingFillStyle; - break; - - } - - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 1)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - /* print the text */ - - let labelPoint; - let fontSize = 10; - - browserCanvasContext.font = fontSize + 'px Courier New'; - - let label; - - /* devTeam */ - - label = thisObject.devTeam.displayName; - - labelPoint = { - x: 10 + devTeamImageSize / 2 - label.length / 2 * fontSize * 0.60, - y: thisObject.container.frame.height / 2 + devTeamImageSize / 2 + fontSize * 0.60 + 5 - }; - - labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - /* bot */ - - label = thisObject.bot.displayName; - - labelPoint = { - x: thisObject.container.frame.width - 10 - botImageSize - 5 + botImageSize / 2 - label.length / 2 * fontSize * 0.60, - y: thisObject.container.frame.height / 2 + devTeamImageSize / 2 + fontSize * 0.60 + 5 - }; - - labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - /* product */ - - fontSize = 10; - - label = thisObject.product.displayName; - - labelPoint = { - x: 65, - y: thisObject.container.frame.height / 2 + 15 - }; - - labelPoint = { - x: thisObject.container.frame.width / 2 - label.length / 2 * fontSize * 0.60, - y: thisObject.container.frame.height / 2 - devTeamImageSize / 2 - fontSize * 0.60 - 5 - }; - - labelPoint = thisObject.container.frame.frameThisPoint(labelPoint); - - browserCanvasContext.font = fontSize + 'px Courier New bold'; - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - /* ------------------- Progress Bars -------------------------- */ - - const ANIMATED_INCREMENT = 5; - const OPACITY_INCREMENT = 0.05; - const OPACITY_MIN = 0.1; - - let point1; - let point2; - let point3; - let point4; - - /* We draw here the Market Progress Bar. */ - - /* Animate */ - - if (marketFileProgressBar.animatedValue < marketFileProgressBar.value) { - - marketFileProgressBar.animatedValue = marketFileProgressBar.animatedValue + ANIMATED_INCREMENT; - marketFileProgressBar.opacity = marketFileProgressBar.opacity + OPACITY_INCREMENT; - - } - - if (marketFileProgressBar.animatedValue >= 100) { - - marketFileProgressBar.animatedValue = 100; - - marketFileProgressBar.opacity = marketFileProgressBar.opacity - OPACITY_INCREMENT; - if (marketFileProgressBar.opacity < OPACITY_MIN) { marketFileProgressBar.opacity = OPACITY_MIN;} - - marketFileProgressBar.fillStyle = LOADED_FILL_STYLE.replace('@Opacity', marketFileProgressBar.opacity.toString()); - marketFileProgressBar.strokeStyle = LOADED_STROKE_STYLE.replace('@Opacity', marketFileProgressBar.opacity.toString()); - - changeStatusTo(PRODUCT_CARD_STATUS.ON); - } - - point1 = { - x: 0, - y: thisObject.container.frame.height - 1 - }; - - point2 = { - x: thisObject.container.frame.width * marketFileProgressBar.animatedValue / 100, - y: thisObject.container.frame.height - 1 - }; - - point3 = { - x: thisObject.container.frame.width * marketFileProgressBar.animatedValue / 100, - y: thisObject.container.frame.height - 3 - }; - - point4 = { - x: 0, - y: thisObject.container.frame.height - 3 - }; - - /* Now the transformations. */ - - 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.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.closePath(); - - browserCanvasContext.fillStyle = marketFileProgressBar.fillStyle.replace('@Opacity', marketFileProgressBar.opacity.toString()); - browserCanvasContext.strokeStyle = marketFileProgressBar.strokeStyle.replace('@Opacity', marketFileProgressBar.opacity.toString()); - - browserCanvasContext.fill(); - browserCanvasContext.lineWidth = 0.1; - browserCanvasContext.stroke(); - - /* We draw here the Daily Progress Bar. */ - - /* Animate */ - - if (dailyFileProgressBar.animatedValue < dailyFileProgressBar.value) { - - dailyFileProgressBar.animatedValue = dailyFileProgressBar.animatedValue + ANIMATED_INCREMENT; - dailyFileProgressBar.opacity = dailyFileProgressBar.opacity + OPACITY_INCREMENT; - - } - - if (dailyFileProgressBar.animatedValue >= 100) { - - dailyFileProgressBar.animatedValue = 100; - - dailyFileProgressBar.opacity = dailyFileProgressBar.opacity - OPACITY_INCREMENT; - if (dailyFileProgressBar.opacity < OPACITY_MIN) { dailyFileProgressBar.opacity = OPACITY_MIN; } - - dailyFileProgressBar.fillStyle = LOADED_FILL_STYLE.replace('@Opacity', dailyFileProgressBar.opacity.toString()); - dailyFileProgressBar.strokeStyle = LOADED_STROKE_STYLE.replace('@Opacity', dailyFileProgressBar.opacity.toString()); - - changeStatusTo(PRODUCT_CARD_STATUS.ON); - } - - point1 = { - x: 0, - y: thisObject.container.frame.height - 4 - }; - - point2 = { - x: thisObject.container.frame.width * dailyFileProgressBar.animatedValue / 100, - y: thisObject.container.frame.height - 4 - }; - - point3 = { - x: thisObject.container.frame.width * dailyFileProgressBar.animatedValue / 100, - y: thisObject.container.frame.height - 6 - }; - - point4 = { - x: 0, - y: thisObject.container.frame.height - 6 - }; - - /* Now the transformations. */ - - 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.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.closePath(); - - browserCanvasContext.fillStyle = dailyFileProgressBar.fillStyle.replace('@Opacity', dailyFileProgressBar.opacity.toString()); - browserCanvasContext.strokeStyle = dailyFileProgressBar.strokeStyle.replace('@Opacity', dailyFileProgressBar.opacity.toString()); - - browserCanvasContext.fill(); - browserCanvasContext.lineWidth = 0.1; - browserCanvasContext.stroke(); - - /* We draw here the Single File Progress Bar. */ - - /* Animate */ - - if (singleFileProgressBar.animatedValue < singleFileProgressBar.value) { - - singleFileProgressBar.animatedValue = singleFileProgressBar.animatedValue + ANIMATED_INCREMENT; - singleFileProgressBar.opacity = singleFileProgressBar.opacity + OPACITY_INCREMENT; - - } - - if (singleFileProgressBar.animatedValue >= 100) { - - singleFileProgressBar.animatedValue = 100; - - singleFileProgressBar.opacity = singleFileProgressBar.opacity - OPACITY_INCREMENT; - if (singleFileProgressBar.opacity < OPACITY_MIN) { singleFileProgressBar.opacity = OPACITY_MIN; } - - singleFileProgressBar.fillStyle = LOADED_FILL_STYLE.replace('@Opacity', singleFileProgressBar.opacity.toString()); - singleFileProgressBar.strokeStyle = LOADED_STROKE_STYLE.replace('@Opacity', singleFileProgressBar.opacity.toString()); - - changeStatusTo(PRODUCT_CARD_STATUS.ON); - - } - - point1 = { - x: 0, - y: thisObject.container.frame.height - 7 - }; - - point2 = { - x: thisObject.container.frame.width * singleFileProgressBar.animatedValue / 100, - y: thisObject.container.frame.height - 7 - }; - - point3 = { - x: thisObject.container.frame.width * singleFileProgressBar.animatedValue / 100, - y: thisObject.container.frame.height - 9 - }; - - point4 = { - x: 0, - y: thisObject.container.frame.height - 9 - }; - - /* Now the transformations. */ - - 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.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.closePath(); - - browserCanvasContext.fillStyle = singleFileProgressBar.fillStyle.replace('@Opacity', singleFileProgressBar.opacity.toString()); - browserCanvasContext.strokeStyle = singleFileProgressBar.strokeStyle.replace('@Opacity', singleFileProgressBar.opacity.toString()); - - browserCanvasContext.fill(); - browserCanvasContext.lineWidth = 0.1; - browserCanvasContext.stroke(); - - /* We draw here the File Sequence Progress Bar. */ - - /* Animate */ - - if (fileSequenceProgressBar.animatedValue < fileSequenceProgressBar.value) { - - fileSequenceProgressBar.animatedValue = fileSequenceProgressBar.animatedValue + ANIMATED_INCREMENT; - fileSequenceProgressBar.opacity = fileSequenceProgressBar.opacity + OPACITY_INCREMENT; - - } - - if (fileSequenceProgressBar.animatedValue >= 100) { - - fileSequenceProgressBar.animatedValue = 100; - - fileSequenceProgressBar.opacity = fileSequenceProgressBar.opacity - OPACITY_INCREMENT; - if (fileSequenceProgressBar.opacity < OPACITY_MIN) { fileSequenceProgressBar.opacity = OPACITY_MIN; } - - fileSequenceProgressBar.fillStyle = LOADED_FILL_STYLE.replace('@Opacity', fileSequenceProgressBar.opacity.toString()); - fileSequenceProgressBar.strokeStyle = LOADED_STROKE_STYLE.replace('@Opacity', fileSequenceProgressBar.opacity.toString()); - - changeStatusTo(PRODUCT_CARD_STATUS.ON); - } - - point1 = { - x: 0, - y: thisObject.container.frame.height - 10 - }; - - point2 = { - x: thisObject.container.frame.width * fileSequenceProgressBar.animatedValue / 100, - y: thisObject.container.frame.height - 10 - }; - - point3 = { - x: thisObject.container.frame.width * fileSequenceProgressBar.animatedValue / 100, - y: thisObject.container.frame.height - 11 - }; - - point4 = { - x: 0, - y: thisObject.container.frame.height - 11 - }; - - /* Now the transformations. */ - - 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.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.closePath(); - - browserCanvasContext.fillStyle = fileSequenceProgressBar.fillStyle.replace('@Opacity', fileSequenceProgressBar.opacity.toString()); - browserCanvasContext.strokeStyle = fileSequenceProgressBar.strokeStyle.replace('@Opacity', fileSequenceProgressBar.opacity.toString()); - - browserCanvasContext.fill(); - browserCanvasContext.lineWidth = 0.1; - browserCanvasContext.stroke(); - } -} \ No newline at end of file diff --git a/ProductStorage.js b/ProductStorage.js deleted file mode 100644 index 1893beffc..000000000 --- a/ProductStorage.js +++ /dev/null @@ -1,426 +0,0 @@ - -function newProductStorage(pName) { - - const MODULE_NAME = "Product Storage"; - const INFO_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - 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 = { - - marketFiles: [], - dailyFiles: [], - singleFile: [], - fileSequence: [], - - setDatetime: setDatetime, - setTimePeriod: setTimePeriod, - - eventHandler: undefined, - initialize: initialize - - } - - thisObject.eventHandler = newEventHandler(); - - /* We name the event Handler to easy debugging. */ - - thisObject.eventHandler.name = "Storage-" + pName; - - let datetime; - let timePeriod; - - return thisObject; - - function initialize(pDevTeam, pBot, pProduct, pExchange, pMarket, pDatetime, pTimePeriod, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - datetime = pDatetime; - timePeriod = pTimePeriod; - - let dataSetsToLoad = 0; - let dataSetsLoaded = 0; - - for (let i = 0; i < pProduct.dataSets.length; i++) { - - let thisSet = pProduct.dataSets[i]; - - switch (thisSet.type) { - case 'Market Files': { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Market Files -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - let marketFiles = newMarketFiles(); - marketFiles.initialize(pDevTeam, pBot, pProduct, thisSet, pExchange, pMarket, onMarketFileReady); - thisObject.marketFiles.push(marketFiles); - dataSetsToLoad++; - } - break; - - case 'Daily Files': { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Daily Files -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - let dailyFiles = newDailyFiles(); - dailyFiles.initialize(pDevTeam, pBot, pProduct, thisSet, pExchange, pMarket, pDatetime, pTimePeriod, onDailyFileReady); - thisObject.dailyFiles.push(dailyFiles); - dataSetsToLoad++; - } - break; - - case 'Single File': { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Single File -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - let singleFile = newSingleFile(); - singleFile.initialize(pDevTeam, pBot, pProduct, thisSet, pExchange, pMarket, onSingleFileReady); - thisObject.singleFile.push(singleFile); - dataSetsToLoad++; - } - break; - - case 'File Sequence': { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> File Sequence -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - let fileSequence = newFileSequence(); - fileSequence.initialize(pDevTeam, pBot, pProduct, thisSet, pExchange, pMarket, onFileSequenceReady); - thisObject.fileSequence.push(fileSequence); - dataSetsToLoad++; - } - break; - } - - function onMarketFileReady(err, pCaller) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onMarketFileReady -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onMarketFileReady -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onMarketFileReady -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onMarketFileReady -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onMarketFileReady -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onMarketFileReady -> err.message = " + err.message); } - - callBackFunction(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onMarketFileReady -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - let event = { - totalValue: pCaller.getExpectedFiles(), - currentValue: pCaller.getFilesLoaded() - } - - thisObject.eventHandler.raiseEvent('Market File Loaded', event); - - if (event.currentValue === event.totalValue) { - - dataSetsLoaded++; - - checkInitializeComplete(); - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onMarketFileReady -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function onDailyFileReady(err, pCaller) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onDailyFileReady -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onDailyFileReady -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onDailyFileReady -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onDailyFileReady -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onDailyFileReady -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onDailyFileReady -> err.message = " + err.message); } - - callBackFunction(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onDailyFileReady -> Received Unexpected Response."); } - 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) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onDailyFileReady -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function onSingleFileReady(err, pCaller) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSingleFileReady -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSingleFileReady -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSingleFileReady -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSingleFileReady -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSingleFileReady -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSingleFileReady -> err.message = " + err.message); } - - callBackFunction(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onSingleFileReady -> Received Unexpected Response."); } - 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) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onSingleFileReady -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function onFileSequenceReady(err, pCaller) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - case GLOBAL.CUSTOM_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Received CUSTOM FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> err.message = " + err.message); } - - callBackFunction(err); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileSequenceReady -> Received Unexpected Response."); } - callBackFunction(err); - return; - } - } - - let event = { - totalValue: pCaller.getExpectedFiles(), - currentValue: pCaller.getFilesLoaded() - } - - thisObject.eventHandler.raiseEvent('File Sequence Loaded', event); - - if (event.currentValue === event.totalValue) { - - dataSetsLoaded++; - - checkInitializeComplete(); - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onFileSequenceReady -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function checkInitializeComplete() { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] checkInitializeComplete -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] checkInitializeComplete -> key = " + pDevTeam.codeName + "-" + pBot.codeName + "-" + pProduct.codeName); } - - if (dataSetsLoaded === dataSetsToLoad) { - - callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE); - - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> checkInitializeComplete -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function setDatetime(pDatetime) { - - if (INFO_LOG === true) { logger.write("[INFO] setDatetime -> Entering function."); } - - /* 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 = pDatetime; - - if (currentDate !== newDate) { - - if (timePeriod <= _1_HOUR_IN_MILISECONDS) { - - for (let i = 0; i < thisObject.dailyFiles.length; i++) { - - thisObject.dailyFiles[i].setDatetime(pDatetime); - } - } - } - - } - - function setTimePeriod(pTimePeriod) { - - if (INFO_LOG === true) { logger.write("[INFO] setTimePeriod -> Entering function."); } - - /* We are going to filter out the cases in which the timePeriod received is the same that the one we already know. */ - - if (timePeriod !== pTimePeriod) { - - timePeriod = pTimePeriod; - - if (timePeriod <= _1_HOUR_IN_MILISECONDS) { - - for (let i = 0; i < thisObject.dailyFiles.length; i++) { - - thisObject.dailyFiles[i].setTimePeriod(pTimePeriod, datetime); - } - } - } - } -} - diff --git a/Project.njsproj b/Project.njsproj index 220a41955..e2492be4c 100644 --- a/Project.njsproj +++ b/Project.njsproj @@ -4,6 +4,7 @@ $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) Project browser + Debug|Any CPU @@ -29,108 +30,67 @@ true - - + + Code + + + Code + + + Code + - + + + + + + + + + + + Code - + Code - - + Code - + Code - + Code - + + Code - + Code - + Code - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - Code - - - - - - - - - - - Code - - + Code - - - - - - - - - - Code - - - Code - - - Code - - - - - - - + + - - - - + + + + + diff --git a/Scripts/AppLoader.js b/Scripts/AppLoader.js new file mode 100644 index 000000000..d5ee032fc --- /dev/null +++ b/Scripts/AppLoader.js @@ -0,0 +1,158 @@ +function newAppLoader() { + + const MODULE_NAME = "App Loader"; + const INFO_LOG = false; + const ERROR_LOG = true; + const INTENSIVE_LOG = false; + const logger = newWebDebugLog(); + logger.fileName = MODULE_NAME; + + let thisObject = { + loadModules: loadModules + }; + + return thisObject; + + function loadModules() { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> Entering function."); } + + let modulesArray = [ + + /* CloudWebScripts */ + + /* Plotters */ + + /* PlotterPanels */ + + "Globals.js", + + "BottomSpace/DeleteTradingHistory.js", + "BottomSpace/ChartAspectRatio.js", + "BottomSpace/PlayStopButton.js", + + "TopSpace/CompanyLogo.js", + "TopSpace/CurrentStartMode.js", + "TopSpace/CurrentProcess.js", + "TopSpace/CurrentBot.js", + "TopSpace/DevTeam.js", + "TopSpace/EndUser.js", + "TopSpace/Login.js", + + "Panels/TimeControlPanel.js", + "Panels/ProductsPanel.js", + + "Spaces/BottomSpace.js", + "Spaces/TopSpace.js", + "Spaces/PanelsSpace.js", + "Spaces/ChartSpace.js", + "Spaces/FloatingSpace.js", + + "Files/SingleFile.js", + "Files/FileCloud.js", + "Files/MarketFiles.js", + "Files/DailyFiles.js", + "Files/FileCursor.js", + "Files/FileSequence.js", + + "FloatingSpace/NoteSets.js", + "FloatingSpace/Note.js", + "FloatingSpace/ProfileBalls.js", + "FloatingSpace/ProfileBall.js", + "FloatingSpace/FloatingObject.js", + "FloatingSpace/FloatingLayer.js", + + "Exchange/ExchangeAPI.js", + "CloudVM/CloudSupport.js", + "CloudVM/CloudRequire.js", + "CloudVM/WebFS.js", + "CloudVM/CloudVM.js", + + "Scales/ChartGrid.js", + "Scales/RightScale.js", + + "BreakpointsBar.js", + "Plotter.js", + "PlotterPanel.js", + + "PlottersManager.js", + "ProductStorage.js", + "CompetitionStorage.js", + "ProductCard.js", + "Ecosystem.js", + + "SplashScreen.js", + "Canvas.js", + "TextButton.js", + "ImageButton.js", + "EventHandler.js", + "Frame.js", + + "ViewPort.js", + "TimeMachine.js", + "Candle.js", + "VolumeBar.js", + "ChartUtilities.js", + "TimelineChart.js", + + "Animation.js", + + "Container.js", + "Displace.js", + + "Azure/azure-storage.blob.js", + "TimeLineCoordinateSystem.js", + "Utilities.js", + "Dashboard.js" + ]; + + let downloadedCounter = 0; + + for (let i = 0; i < modulesArray.length; i++) { + + let path = window.canvasApp.urlPrefix + modulesArray[i]; + + REQUIREJS([path], onRequired); + + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> Module Requested."); } + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> path = " + path); } + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> total requested = " + (i + 1)); } + + function onRequired(pModule) { + + try { + + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> onRequired -> Entering function."); } + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> onRequired -> Module Downloaded."); } + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> onRequired -> path = " + path); } + + downloadedCounter++; + + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> onRequired -> downloadedCounter = " + downloadedCounter); } + + if (downloadedCounter === modulesArray.length) { + + if (INFO_LOG === true) { logger.write("[INFO] loadModules -> onRequired -> Starting Advanced Algos Platform."); } + + let dashboard = newDashboard(); + + dashboard.start(); + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] loadModules -> onRequired -> err = " + err); } + + } + } + } + + } catch (err) { + + if (ERROR_LOG === true) { logger.write("[ERROR] loadModules -> err = " + err); } + + } + } +} \ No newline at end of file diff --git a/Scripts/AppPreLoader.js b/Scripts/AppPreLoader.js new file mode 100644 index 000000000..7faa9c053 --- /dev/null +++ b/Scripts/AppPreLoader.js @@ -0,0 +1,138 @@ + +/* Callbacks default responses. */ + +let GLOBAL = {}; + +GLOBAL.DEFAULT_OK_RESPONSE = { + result: "Ok", + message: "Operation Succeeded" +}; + +GLOBAL.DEFAULT_FAIL_RESPONSE = { + result: "Fail", + message: "Operation Failed" +}; + +GLOBAL.DEFAULT_RETRY_RESPONSE = { + result: "Retry", + message: "Retry Later" +}; + +GLOBAL.CUSTOM_OK_RESPONSE = { + result: "Ok, but check Message", + message: "Custom Message" +}; + +GLOBAL.CUSTOM_FAIL_RESPONSE = { + result: "Fail Because", + message: "Custom Message" +}; + +let browserCanvas; // This is the canvas object of the browser. + +function spacePad(str, max) { + str = str.toString(); + return str.length < max ? spacePad(" " + str, max) : str; +} + +function loadAdvancedAlgosPlatform() { + + const MODULE_NAME = "App Pre-Loader"; + const INFO_LOG = false; + const ERROR_LOG = true; + + if (INFO_LOG === true) { console.log(spacePad(MODULE_NAME, 50) + " : " + "[INFO] loadAdvancedAlgosPlatform -> Entering function."); } + + /* The first thing to do here is to add the canvas where all the action is going to happen. */ + + let canvas = document.createElement('canvas'); + + canvas.id = "canvas"; + canvas.width = 1400; + canvas.height = 600; + canvas.style.border = "0"; + canvas.style = "position:absolute; top:0px; left:0px; z-index:1"; + + let canvasApp = document.getElementById('canvasApp'); + canvasApp.appendChild(canvas); + + browserCanvas = document.getElementById('canvas'); + + browserCanvas.width = window.innerWidth; + browserCanvas.height = window.innerHeight - window.canvasApp.topMargin; + browserCanvas.style.border = "none"; + + browserCanvas.style.top = window.canvasApp.topMargin + 'px'; + + loadDebugModule(); + + function loadDebugModule() { + + if (INFO_LOG === true) { console.log(spacePad(MODULE_NAME, 50) + " : " + "[INFO] loadDebugModule -> Entering function."); } + + let path = window.canvasApp.urlPrefix + "WebDebugLog.js"; + + REQUIREJS([path], onRequired); + + function onRequired(pModule) { + + if (INFO_LOG === true) { console.log(spacePad(MODULE_NAME, 50) + " : " + "[INFO] " + path + " downloaded."); } + + loadModules(); + + } + } + + /* And Finally, we start loading all the scripts we will inmediatelly need. */ + + function loadModules() { + + if (INFO_LOG === true) { console.log(spacePad(MODULE_NAME, 50) + " : " + "[INFO] loadModules -> Entering function."); } + + let path = window.canvasApp.urlPrefix + "Scripts/AppLoader.js"; + + REQUIREJS([path], onRequired); + + function onRequired(pModule) { + + if (INFO_LOG === true) { console.log(spacePad(MODULE_NAME, 50) + " : " + "[INFO] " + path + " downloaded."); } + + let APP_LOADER_MODULE = newAppLoader(); + APP_LOADER_MODULE.loadModules(); + + } + } +} + +function callServer(pContentToSend, pPath, callBackFunction) { + + let xhttp = new XMLHttpRequest(); + xhttp.onreadystatechange = function () { + if (this.readyState === 4 && this.status === 200) { + + callBackFunction(xhttp.responseText); + + } + }; + + if (pContentToSend === undefined) { + + xhttp.open("GET", pPath, true); + xhttp.send(); + + } else { + + try { + + let blob = new Blob([pContentToSend], { type: 'text/plain' }); + + xhttp.open("POST", pPath, true); + xhttp.send(blob); + + } catch (err) { + + if (ERROR_LOG === true) { console.log(spacePad(MODULE_NAME, 50) + " : " + "[ERROR] callServer -> err.message = " & err.message); } + + } + } +} diff --git a/Scripts/require.js b/Scripts/require.js new file mode 100644 index 000000000..f9476f532 --- /dev/null +++ b/Scripts/require.js @@ -0,0 +1,2145 @@ +/** vim: et:ts=4:sw=4:sts=4 + * @license RequireJS 2.3.5 Copyright jQuery Foundation and other contributors. + * Released under MIT license, https://github.com/REQUIREJS/REQUIREJS/blob/master/LICENSE + */ +//Not using strict: uneven strict support in browsers, #392, and causes +//problems with REQUIREJS.exec()/transpiler plugins that may not be strict. +/*jslint regexp: true, nomen: true, sloppy: true */ +/*global window, navigator, document, importScripts, setTimeout, opera */ + +var REQUIREJS, REQUIRE, DEFINE; +(function (global, setTimeout) { + var req, s, head, baseElement, dataMain, src, + interactiveScript, currentlyAddingScript, mainScript, subPath, + version = '2.3.5', + commentRegExp = /\/\*[\s\S]*?\*\/|([^:"'=]|^)\/\/.*$/mg, + cjsRequireRegExp = /[^.]\s*REQUIRE\s*\(\s*["']([^'"\s]+)["']\s*\)/g, + jsSuffixRegExp = /\.js$/, + currDirRegExp = /^\.\//, + op = Object.prototype, + ostring = op.toString, + hasOwn = op.hasOwnProperty, + isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document), + isWebWorker = !isBrowser && typeof importScripts !== 'undefined', + //PS3 indicates loaded and complete, but need to wait for complete + //specifically. Sequence is 'loading', 'loaded', execution, + // then 'complete'. The UA check is unfortunate, but not sure how + //to feature test w/o causing perf issues. + readyRegExp = isBrowser && navigator.platform === 'PLAYSTATION 3' ? + /^complete$/ : /^(complete|loaded)$/, + defContextName = '_', + //Oh the tragedy, detecting opera. See the usage of isOpera for reason. + isOpera = typeof opera !== 'undefined' && opera.toString() === '[object Opera]', + contexts = {}, + cfg = {}, + globalDefQueue = [], + useInteractive = false; + + //Could match something like ')//comment', do not lose the prefix to comment. + function commentReplace(match, singlePrefix) { + return singlePrefix || ''; + } + + function isFunction(it) { + return ostring.call(it) === '[object Function]'; + } + + function isArray(it) { + return ostring.call(it) === '[object Array]'; + } + + /** + * Helper function for iterating over an array. If the func returns + * a true value, it will break out of the loop. + */ + function each(ary, func) { + if (ary) { + var i; + for (i = 0; i < ary.length; i += 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + /** + * Helper function for iterating over an array backwards. If the func + * returns a true value, it will break out of the loop. + */ + function eachReverse(ary, func) { + if (ary) { + var i; + for (i = ary.length - 1; i > -1; i -= 1) { + if (ary[i] && func(ary[i], i, ary)) { + break; + } + } + } + } + + function hasProp(obj, prop) { + return hasOwn.call(obj, prop); + } + + function getOwn(obj, prop) { + return hasProp(obj, prop) && obj[prop]; + } + + /** + * Cycles over properties in an object and calls a function for each + * property value. If the function returns a truthy value, then the + * iteration is stopped. + */ + function eachProp(obj, func) { + var prop; + for (prop in obj) { + if (hasProp(obj, prop)) { + if (func(obj[prop], prop)) { + break; + } + } + } + } + + /** + * Simple function to mix in properties from source into target, + * but only if target does not already have a property of the same name. + */ + function mixin(target, source, force, deepStringMixin) { + if (source) { + eachProp(source, function (value, prop) { + if (force || !hasProp(target, prop)) { + if (deepStringMixin && typeof value === 'object' && value && + !isArray(value) && !isFunction(value) && + !(value instanceof RegExp)) { + + if (!target[prop]) { + target[prop] = {}; + } + mixin(target[prop], value, force, deepStringMixin); + } else { + target[prop] = value; + } + } + }); + } + return target; + } + + //Similar to Function.prototype.bind, but the 'this' object is specified + //first, since it is easier to read/figure out what 'this' will be. + function bind(obj, fn) { + return function () { + return fn.apply(obj, arguments); + }; + } + + function scripts() { + return document.getElementsByTagName('script'); + } + + function defaultOnError(err) { + throw err; + } + + //Allow getting a global that is expressed in + //dot notation, like 'a.b.c'. + function getGlobal(value) { + if (!value) { + return value; + } + var g = global; + each(value.split('.'), function (part) { + g = g[part]; + }); + return g; + } + + /** + * Constructs an error with a pointer to an URL with more information. + * @param {String} id the error ID that maps to an ID on a web page. + * @param {String} message human readable error. + * @param {Error} [err] the original error, if there is one. + * + * @returns {Error} + */ + function makeError(id, msg, err, requireModules) { + var e = new Error(msg + '\nhttp://REQUIREJS.org/docs/errors.html#' + id); + e.requireType = id; + e.requireModules = requireModules; + if (err) { + e.originalError = err; + } + return e; + } + + if (typeof DEFINE !== 'undefined') { + //If a DEFINE is already in play via another AMD loader, + //do not overwrite. + return; + } + + if (typeof REQUIREJS !== 'undefined') { + if (isFunction(REQUIREJS)) { + //Do not overwrite an existing REQUIREJS instance. + return; + } + cfg = REQUIREJS; + REQUIREJS = undefined; + } + + //Allow for a REQUIRE config object + if (typeof REQUIRE !== 'undefined' && !isFunction(REQUIRE)) { + //assume it is a config object. + cfg = REQUIRE; + REQUIRE = undefined; + } + + function newContext(contextName) { + var inCheckLoaded, Module, context, handlers, + checkLoadedTimeoutId, + config = { + //Defaults. Do not set a default for map + //config to speed up normalize(), which + //will run faster if there is no default. + waitSeconds: 60, + baseUrl: './', + paths: {}, + bundles: {}, + pkgs: {}, + shim: {}, + config: {} + }, + registry = {}, + //registry of just enabled modules, to speed + //cycle breaking code when lots of modules + //are registered, but not activated. + enabledRegistry = {}, + undefEvents = {}, + defQueue = [], + defined = {}, + urlFetched = {}, + bundlesMap = {}, + requireCounter = 1, + unnormalizedCounter = 1; + + /** + * Trims the . and .. from an array of path segments. + * It will keep a leading path segment if a .. will become + * the first path segment, to help with module name lookups, + * which act like paths, but can be remapped. But the end result, + * all paths that use this function should look normalized. + * NOTE: this method MODIFIES the input array. + * @param {Array} ary the array of path segments. + */ + function trimDots(ary) { + var i, part; + for (i = 0; i < ary.length; i++) { + part = ary[i]; + if (part === '.') { + ary.splice(i, 1); + i -= 1; + } else if (part === '..') { + // If at the start, or previous value is still .., + // keep them so that when converted to a path it may + // still work when converted to a path, even though + // as an ID it is less than ideal. In larger point + // releases, may be better to just kick out an error. + if (i === 0 || (i === 1 && ary[2] === '..') || ary[i - 1] === '..') { + continue; + } else if (i > 0) { + ary.splice(i - 1, 2); + i -= 2; + } + } + } + } + + /** + * Given a relative module name, like ./something, normalize it to + * a real name that can be mapped to a path. + * @param {String} name the relative name + * @param {String} baseName a real name that the name arg is relative + * to. + * @param {Boolean} applyMap apply the map config to the value. Should + * only be done if this normalization is for a dependency ID. + * @returns {String} normalized name + */ + function normalize(name, baseName, applyMap) { + var pkgMain, mapValue, nameParts, i, j, nameSegment, lastIndex, + foundMap, foundI, foundStarMap, starI, normalizedBaseParts, + baseParts = (baseName && baseName.split('/')), + map = config.map, + starMap = map && map['*']; + + //Adjust any relative paths. + if (name) { + name = name.split('/'); + lastIndex = name.length - 1; + + // If wanting node ID compatibility, strip .js from end + // of IDs. Have to do this here, and not in nameToUrl + // because node allows either .js or non .js to map + // to same file. + if (config.nodeIdCompat && jsSuffixRegExp.test(name[lastIndex])) { + name[lastIndex] = name[lastIndex].replace(jsSuffixRegExp, ''); + } + + // Starts with a '.' so need the baseName + if (name[0].charAt(0) === '.' && baseParts) { + //Convert baseName to array, and lop off the last part, + //so that . matches that 'directory' and not name of the baseName's + //module. For instance, baseName of 'one/two/three', maps to + //'one/two/three.js', but we want the directory, 'one/two' for + //this normalization. + normalizedBaseParts = baseParts.slice(0, baseParts.length - 1); + name = normalizedBaseParts.concat(name); + } + + trimDots(name); + name = name.join('/'); + } + + //Apply map config if available. + if (applyMap && map && (baseParts || starMap)) { + nameParts = name.split('/'); + + outerLoop: for (i = nameParts.length; i > 0; i -= 1) { + nameSegment = nameParts.slice(0, i).join('/'); + + if (baseParts) { + //Find the longest baseName segment match in the config. + //So, do joins on the biggest to smallest lengths of baseParts. + for (j = baseParts.length; j > 0; j -= 1) { + mapValue = getOwn(map, baseParts.slice(0, j).join('/')); + + //baseName segment has config, find if it has one for + //this name. + if (mapValue) { + mapValue = getOwn(mapValue, nameSegment); + if (mapValue) { + //Match, update name to the new value. + foundMap = mapValue; + foundI = i; + break outerLoop; + } + } + } + } + + //Check for a star map match, but just hold on to it, + //if there is a shorter segment match later in a matching + //config, then favor over this star map. + if (!foundStarMap && starMap && getOwn(starMap, nameSegment)) { + foundStarMap = getOwn(starMap, nameSegment); + starI = i; + } + } + + if (!foundMap && foundStarMap) { + foundMap = foundStarMap; + foundI = starI; + } + + if (foundMap) { + nameParts.splice(0, foundI, foundMap); + name = nameParts.join('/'); + } + } + + // If the name points to a package's name, use + // the package main instead. + pkgMain = getOwn(config.pkgs, name); + + return pkgMain ? pkgMain : name; + } + + function removeScript(name) { + if (isBrowser) { + each(scripts(), function (scriptNode) { + if (scriptNode.getAttribute('data-requiremodule') === name && + scriptNode.getAttribute('data-requirecontext') === context.contextName) { + scriptNode.parentNode.removeChild(scriptNode); + return true; + } + }); + } + } + + function hasPathFallback(id) { + var pathConfig = getOwn(config.paths, id); + if (pathConfig && isArray(pathConfig) && pathConfig.length > 1) { + //Pop off the first array value, since it failed, and + //retry + pathConfig.shift(); + context.REQUIRE.undef(id); + + //Custom REQUIRE that does not do map translation, since + //ID is "absolute", already mapped/resolved. + context.makeRequire(null, { + skipMap: true + })([id]); + + return true; + } + } + + //Turns a plugin!resource to [plugin, resource] + //with the plugin being undefined if the name + //did not have a plugin prefix. + function splitPrefix(name) { + var prefix, + index = name ? name.indexOf('!') : -1; + if (index > -1) { + prefix = name.substring(0, index); + name = name.substring(index + 1, name.length); + } + return [prefix, name]; + } + + /** + * Creates a module mapping that includes plugin prefix, module + * name, and path. If parentModuleMap is provided it will + * also normalize the name via REQUIRE.normalize() + * + * @param {String} name the module name + * @param {String} [parentModuleMap] parent module map + * for the module name, used to resolve relative names. + * @param {Boolean} isNormalized: is the ID already normalized. + * This is true if this call is done for a DEFINE() module ID. + * @param {Boolean} applyMap: apply the map config to the ID. + * Should only be true if this map is for a dependency. + * + * @returns {Object} + */ + function makeModuleMap(name, parentModuleMap, isNormalized, applyMap) { + var url, pluginModule, suffix, nameParts, + prefix = null, + parentName = parentModuleMap ? parentModuleMap.name : null, + originalName = name, + isDefine = true, + normalizedName = ''; + + //If no name, then it means it is a REQUIRE call, generate an + //internal name. + if (!name) { + isDefine = false; + name = '_@r' + (requireCounter += 1); + } + + nameParts = splitPrefix(name); + prefix = nameParts[0]; + name = nameParts[1]; + + if (prefix) { + prefix = normalize(prefix, parentName, applyMap); + pluginModule = getOwn(defined, prefix); + } + + //Account for relative paths if there is a base name. + if (name) { + if (prefix) { + if (isNormalized) { + normalizedName = name; + } else if (pluginModule && pluginModule.normalize) { + //Plugin is loaded, use its normalize method. + normalizedName = pluginModule.normalize(name, function (name) { + return normalize(name, parentName, applyMap); + }); + } else { + // If nested plugin references, then do not try to + // normalize, as it will not normalize correctly. This + // places a restriction on resourceIds, and the longer + // term solution is not to normalize until plugins are + // loaded and all normalizations to allow for async + // loading of a loader plugin. But for now, fixes the + // common uses. Details in #1131 + normalizedName = name.indexOf('!') === -1 ? + normalize(name, parentName, applyMap) : + name; + } + } else { + //A regular module. + normalizedName = normalize(name, parentName, applyMap); + + //Normalized name may be a plugin ID due to map config + //application in normalize. The map config values must + //already be normalized, so do not need to redo that part. + nameParts = splitPrefix(normalizedName); + prefix = nameParts[0]; + normalizedName = nameParts[1]; + isNormalized = true; + + url = context.nameToUrl(normalizedName); + } + } + + //If the id is a plugin id that cannot be determined if it needs + //normalization, stamp it with a unique ID so two matching relative + //ids that may conflict can be separate. + suffix = prefix && !pluginModule && !isNormalized ? + '_unnormalized' + (unnormalizedCounter += 1) : + ''; + + return { + prefix: prefix, + name: normalizedName, + parentMap: parentModuleMap, + unnormalized: !!suffix, + url: url, + originalName: originalName, + isDefine: isDefine, + id: (prefix ? + prefix + '!' + normalizedName : + normalizedName) + suffix + }; + } + + function getModule(depMap) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (!mod) { + mod = registry[id] = new context.Module(depMap); + } + + return mod; + } + + function on(depMap, name, fn) { + var id = depMap.id, + mod = getOwn(registry, id); + + if (hasProp(defined, id) && + (!mod || mod.defineEmitComplete)) { + if (name === 'defined') { + fn(defined[id]); + } + } else { + mod = getModule(depMap); + if (mod.error && name === 'error') { + fn(mod.error); + } else { + mod.on(name, fn); + } + } + } + + function onError(err, errback) { + var ids = err.requireModules, + notified = false; + + if (errback) { + errback(err); + } else { + each(ids, function (id) { + var mod = getOwn(registry, id); + if (mod) { + //Set error on module, so it skips timeout checks. + mod.error = err; + if (mod.events.error) { + notified = true; + mod.emit('error', err); + } + } + }); + + if (!notified) { + req.onError(err); + } + } + } + + /** + * Internal method to transfer globalQueue items to this context's + * defQueue. + */ + function takeGlobalQueue() { + //Push all the globalDefQueue items into the context's defQueue + if (globalDefQueue.length) { + each(globalDefQueue, function(queueItem) { + var id = queueItem[0]; + if (typeof id === 'string') { + context.defQueueMap[id] = true; + } + defQueue.push(queueItem); + }); + globalDefQueue = []; + } + } + + handlers = { + 'REQUIRE': function (mod) { + if (mod.REQUIRE) { + return mod.REQUIRE; + } else { + return (mod.REQUIRE = context.makeRequire(mod.map)); + } + }, + 'exports': function (mod) { + mod.usingExports = true; + if (mod.map.isDefine) { + if (mod.exports) { + return (defined[mod.map.id] = mod.exports); + } else { + return (mod.exports = defined[mod.map.id] = {}); + } + } + }, + 'module': function (mod) { + if (mod.module) { + return mod.module; + } else { + return (mod.module = { + id: mod.map.id, + uri: mod.map.url, + config: function () { + return getOwn(config.config, mod.map.id) || {}; + }, + exports: mod.exports || (mod.exports = {}) + }); + } + } + }; + + function cleanRegistry(id) { + //Clean up machinery used for waiting modules. + delete registry[id]; + delete enabledRegistry[id]; + } + + function breakCycle(mod, traced, processed) { + var id = mod.map.id; + + if (mod.error) { + mod.emit('error', mod.error); + } else { + traced[id] = true; + each(mod.depMaps, function (depMap, i) { + var depId = depMap.id, + dep = getOwn(registry, depId); + + //Only force things that have not completed + //being defined, so still in the registry, + //and only if it has not been matched up + //in the module already. + if (dep && !mod.depMatched[i] && !processed[depId]) { + if (getOwn(traced, depId)) { + mod.defineDep(i, defined[depId]); + mod.check(); //pass false? + } else { + breakCycle(dep, traced, processed); + } + } + }); + processed[id] = true; + } + } + + function checkLoaded() { + var err, usingPathFallback, + waitInterval = config.waitSeconds * 1000, + //It is possible to disable the wait interval by using waitSeconds of 0. + expired = waitInterval && (context.startTime + waitInterval) < new Date().getTime(), + noLoads = [], + reqCalls = [], + stillLoading = false, + needCycleCheck = true; + + //Do not bother if this call was a result of a cycle break. + if (inCheckLoaded) { + return; + } + + inCheckLoaded = true; + + //Figure out the state of all the modules. + eachProp(enabledRegistry, function (mod) { + var map = mod.map, + modId = map.id; + + //Skip things that are not enabled or in error state. + if (!mod.enabled) { + return; + } + + if (!map.isDefine) { + reqCalls.push(mod); + } + + if (!mod.error) { + //If the module should be executed, and it has not + //been inited and time is up, remember it. + if (!mod.inited && expired) { + if (hasPathFallback(modId)) { + usingPathFallback = true; + stillLoading = true; + } else { + noLoads.push(modId); + removeScript(modId); + } + } else if (!mod.inited && mod.fetched && map.isDefine) { + stillLoading = true; + if (!map.prefix) { + //No reason to keep looking for unfinished + //loading. If the only stillLoading is a + //plugin resource though, keep going, + //because it may be that a plugin resource + //is waiting on a non-plugin cycle. + return (needCycleCheck = false); + } + } + } + }); + + if (expired && noLoads.length) { + //If wait time expired, throw error of unloaded modules. + err = makeError('timeout', 'Load timeout for modules: ' + noLoads, null, noLoads); + err.contextName = context.contextName; + return onError(err); + } + + //Not expired, check for a cycle. + if (needCycleCheck) { + each(reqCalls, function (mod) { + breakCycle(mod, {}, {}); + }); + } + + //If still waiting on loads, and the waiting load is something + //other than a plugin resource, or there are still outstanding + //scripts, then just try back later. + if ((!expired || usingPathFallback) && stillLoading) { + //Something is still waiting to load. Wait for it, but only + //if a timeout is not already in effect. + if ((isBrowser || isWebWorker) && !checkLoadedTimeoutId) { + checkLoadedTimeoutId = setTimeout(function () { + checkLoadedTimeoutId = 0; + checkLoaded(); + }, 50); + } + } + + inCheckLoaded = false; + } + + Module = function (map) { + this.events = getOwn(undefEvents, map.id) || {}; + this.map = map; + this.shim = getOwn(config.shim, map.id); + this.depExports = []; + this.depMaps = []; + this.depMatched = []; + this.pluginMaps = {}; + this.depCount = 0; + + /* this.exports this.factory + this.depMaps = [], + this.enabled, this.fetched + */ + }; + + Module.prototype = { + init: function (depMaps, factory, errback, options) { + options = options || {}; + + //Do not do more inits if already done. Can happen if there + //are multiple DEFINE calls for the same module. That is not + //a normal, common case, but it is also not unexpected. + if (this.inited) { + return; + } + + this.factory = factory; + + if (errback) { + //Register for errors on this module. + this.on('error', errback); + } else if (this.events.error) { + //If no errback already, but there are error listeners + //on this module, set up an errback to pass to the deps. + errback = bind(this, function (err) { + this.emit('error', err); + }); + } + + //Do a copy of the dependency array, so that + //source inputs are not modified. For example + //"shim" deps are passed in here directly, and + //doing a direct modification of the depMaps array + //would affect that config. + this.depMaps = depMaps && depMaps.slice(0); + + this.errback = errback; + + //Indicate this module has be initialized + this.inited = true; + + this.ignore = options.ignore; + + //Could have option to init this module in enabled mode, + //or could have been previously marked as enabled. However, + //the dependencies are not known until init is called. So + //if enabled previously, now trigger dependencies as enabled. + if (options.enabled || this.enabled) { + //Enable this module and dependencies. + //Will call this.check() + this.enable(); + } else { + this.check(); + } + }, + + defineDep: function (i, depExports) { + //Because of cycles, defined callback for a given + //export can be called more than once. + if (!this.depMatched[i]) { + this.depMatched[i] = true; + this.depCount -= 1; + this.depExports[i] = depExports; + } + }, + + fetch: function () { + if (this.fetched) { + return; + } + this.fetched = true; + + context.startTime = (new Date()).getTime(); + + var map = this.map; + + //If the manager is for a plugin managed resource, + //ask the plugin to load it now. + if (this.shim) { + context.makeRequire(this.map, { + enableBuildCallback: true + })(this.shim.deps || [], bind(this, function () { + return map.prefix ? this.callPlugin() : this.load(); + })); + } else { + //Regular dependency. + return map.prefix ? this.callPlugin() : this.load(); + } + }, + + load: function () { + var url = this.map.url; + + //Regular dependency. + if (!urlFetched[url]) { + urlFetched[url] = true; + context.load(this.map.id, url); + } + }, + + /** + * Checks if the module is ready to DEFINE itself, and if so, + * DEFINE it. + */ + check: function () { + if (!this.enabled || this.enabling) { + return; + } + + var err, cjsModule, + id = this.map.id, + depExports = this.depExports, + exports = this.exports, + factory = this.factory; + + if (!this.inited) { + // Only fetch if not already in the defQueue. + if (!hasProp(context.defQueueMap, id)) { + this.fetch(); + } + } else if (this.error) { + this.emit('error', this.error); + } else if (!this.defining) { + //The factory could trigger another REQUIRE call + //that would result in checking this module to + //DEFINE itself again. If already in the process + //of doing that, skip this work. + this.defining = true; + + if (this.depCount < 1 && !this.defined) { + if (isFunction(factory)) { + //If there is an error listener, favor passing + //to that instead of throwing an error. However, + //only do it for DEFINE()'d modules. REQUIRE + //errbacks should not be called for failures in + //their callbacks (#699). However if a global + //onError is set, use that. + if ((this.events.error && this.map.isDefine) || + req.onError !== defaultOnError) { + try { + exports = context.execCb(id, factory, depExports, exports); + } catch (e) { + err = e; + } + } else { + exports = context.execCb(id, factory, depExports, exports); + } + + // Favor return value over exports. If node/cjs in play, + // then will not have a return value anyway. Favor + // module.exports assignment over exports object. + if (this.map.isDefine && exports === undefined) { + cjsModule = this.module; + if (cjsModule) { + exports = cjsModule.exports; + } else if (this.usingExports) { + //exports already set the defined value. + exports = this.exports; + } + } + + if (err) { + err.requireMap = this.map; + err.requireModules = this.map.isDefine ? [this.map.id] : null; + err.requireType = this.map.isDefine ? 'DEFINE' : 'REQUIRE'; + return onError((this.error = err)); + } + + } else { + //Just a literal value + exports = factory; + } + + this.exports = exports; + + if (this.map.isDefine && !this.ignore) { + defined[id] = exports; + + if (req.onResourceLoad) { + var resLoadMaps = []; + each(this.depMaps, function (depMap) { + resLoadMaps.push(depMap.normalizedMap || depMap); + }); + req.onResourceLoad(context, this.map, resLoadMaps); + } + } + + //Clean up + cleanRegistry(id); + + this.defined = true; + } + + //Finished the DEFINE stage. Allow calling check again + //to allow DEFINE notifications below in the case of a + //cycle. + this.defining = false; + + if (this.defined && !this.defineEmitted) { + this.defineEmitted = true; + this.emit('defined', this.exports); + this.defineEmitComplete = true; + } + + } + }, + + callPlugin: function () { + var map = this.map, + id = map.id, + //Map already normalized the prefix. + pluginMap = makeModuleMap(map.prefix); + + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(pluginMap); + + on(pluginMap, 'defined', bind(this, function (plugin) { + var load, normalizedMap, normalizedMod, + bundleId = getOwn(bundlesMap, this.map.id), + name = this.map.name, + parentName = this.map.parentMap ? this.map.parentMap.name : null, + localRequire = context.makeRequire(map.parentMap, { + enableBuildCallback: true + }); + + //If current map is not normalized, wait for that + //normalized name to load instead of continuing. + if (this.map.unnormalized) { + //Normalize the ID if the plugin allows it. + if (plugin.normalize) { + name = plugin.normalize(name, function (name) { + return normalize(name, parentName, true); + }) || ''; + } + + //prefix and name should already be normalized, no need + //for applying map config again either. + normalizedMap = makeModuleMap(map.prefix + '!' + name, + this.map.parentMap, + true); + on(normalizedMap, + 'defined', bind(this, function (value) { + this.map.normalizedMap = normalizedMap; + this.init([], function () { return value; }, null, { + enabled: true, + ignore: true + }); + })); + + normalizedMod = getOwn(registry, normalizedMap.id); + if (normalizedMod) { + //Mark this as a dependency for this plugin, so it + //can be traced for cycles. + this.depMaps.push(normalizedMap); + + if (this.events.error) { + normalizedMod.on('error', bind(this, function (err) { + this.emit('error', err); + })); + } + normalizedMod.enable(); + } + + return; + } + + //If a paths config, then just load that file instead to + //resolve the plugin, as it is built into that paths layer. + if (bundleId) { + this.map.url = context.nameToUrl(bundleId); + this.load(); + return; + } + + load = bind(this, function (value) { + this.init([], function () { return value; }, null, { + enabled: true + }); + }); + + load.error = bind(this, function (err) { + this.inited = true; + this.error = err; + err.requireModules = [id]; + + //Remove temp unnormalized modules for this module, + //since they will never be resolved otherwise now. + eachProp(registry, function (mod) { + if (mod.map.id.indexOf(id + '_unnormalized') === 0) { + cleanRegistry(mod.map.id); + } + }); + + onError(err); + }); + + //Allow plugins to load other code without having to know the + //context or how to 'complete' the load. + load.fromText = bind(this, function (text, textAlt) { + /*jslint evil: true */ + var moduleName = map.name, + moduleMap = makeModuleMap(moduleName), + hasInteractive = useInteractive; + + //As of 2.1.0, support just passing the text, to reinforce + //fromText only being called once per resource. Still + //support old style of passing moduleName but discard + //that moduleName in favor of the internal ref. + if (textAlt) { + text = textAlt; + } + + //Turn off interactive script matching for IE for any DEFINE + //calls in the text, then turn it back on at the end. + if (hasInteractive) { + useInteractive = false; + } + + //Prime the system by creating a module instance for + //it. + getModule(moduleMap); + + //Transfer any config to this other module. + if (hasProp(config.config, id)) { + config.config[moduleName] = config.config[id]; + } + + try { + req.exec(text); + } catch (e) { + return onError(makeError('fromtexteval', + 'fromText eval for ' + id + + ' failed: ' + e, + e, + [id])); + } + + if (hasInteractive) { + useInteractive = true; + } + + //Mark this as a dependency for the plugin + //resource + this.depMaps.push(moduleMap); + + //Support anonymous modules. + context.completeLoad(moduleName); + + //Bind the value of that module to the value for this + //resource ID. + localRequire([moduleName], load); + }); + + //Use parentName here since the plugin's name is not reliable, + //could be some weird string with no path that actually wants to + //reference the parentName's path. + plugin.load(map.name, localRequire, load, config); + })); + + context.enable(pluginMap, this); + this.pluginMaps[pluginMap.id] = pluginMap; + }, + + enable: function () { + enabledRegistry[this.map.id] = this; + this.enabled = true; + + //Set flag mentioning that the module is enabling, + //so that immediate calls to the defined callbacks + //for dependencies do not trigger inadvertent load + //with the depCount still being zero. + this.enabling = true; + + //Enable each dependency + each(this.depMaps, bind(this, function (depMap, i) { + var id, mod, handler; + + if (typeof depMap === 'string') { + //Dependency needs to be converted to a depMap + //and wired up to this module. + depMap = makeModuleMap(depMap, + (this.map.isDefine ? this.map : this.map.parentMap), + false, + !this.skipMap); + this.depMaps[i] = depMap; + + handler = getOwn(handlers, depMap.id); + + if (handler) { + this.depExports[i] = handler(this); + return; + } + + this.depCount += 1; + + on(depMap, 'defined', bind(this, function (depExports) { + if (this.undefed) { + return; + } + this.defineDep(i, depExports); + this.check(); + })); + + if (this.errback) { + on(depMap, 'error', bind(this, this.errback)); + } else if (this.events.error) { + // No direct errback on this module, but something + // else is listening for errors, so be sure to + // propagate the error correctly. + on(depMap, 'error', bind(this, function(err) { + this.emit('error', err); + })); + } + } + + id = depMap.id; + mod = registry[id]; + + //Skip special modules like 'REQUIRE', 'exports', 'module' + //Also, don't call enable if it is already enabled, + //important in circular dependency cases. + if (!hasProp(handlers, id) && mod && !mod.enabled) { + context.enable(depMap, this); + } + })); + + //Enable each plugin that is used in + //a dependency + eachProp(this.pluginMaps, bind(this, function (pluginMap) { + var mod = getOwn(registry, pluginMap.id); + if (mod && !mod.enabled) { + context.enable(pluginMap, this); + } + })); + + this.enabling = false; + + this.check(); + }, + + on: function (name, cb) { + var cbs = this.events[name]; + if (!cbs) { + cbs = this.events[name] = []; + } + cbs.push(cb); + }, + + emit: function (name, evt) { + each(this.events[name], function (cb) { + cb(evt); + }); + if (name === 'error') { + //Now that the error handler was triggered, remove + //the listeners, since this broken Module instance + //can stay around for a while in the registry. + delete this.events[name]; + } + } + }; + + function callGetModule(args) { + //Skip modules already defined. + if (!hasProp(defined, args[0])) { + getModule(makeModuleMap(args[0], null, true)).init(args[1], args[2]); + } + } + + function removeListener(node, func, name, ieName) { + //Favor detachEvent because of IE9 + //issue, see attachEvent/addEventListener comment elsewhere + //in this file. + if (node.detachEvent && !isOpera) { + //Probably IE. If not it will throw an error, which will be + //useful to know. + if (ieName) { + node.detachEvent(ieName, func); + } + } else { + node.removeEventListener(name, func, false); + } + } + + /** + * Given an event from a script node, get the REQUIREJS info from it, + * and then removes the event listeners on the node. + * @param {Event} evt + * @returns {Object} + */ + function getScriptData(evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + var node = evt.currentTarget || evt.srcElement; + + //Remove the listeners once here. + removeListener(node, context.onScriptLoad, 'load', 'onreadystatechange'); + removeListener(node, context.onScriptError, 'error'); + + return { + node: node, + id: node && node.getAttribute('data-requiremodule') + }; + } + + function intakeDefines() { + var args; + + //Any defined modules in the global queue, intake them now. + takeGlobalQueue(); + + //Make sure any remaining defQueue items get properly processed. + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + return onError(makeError('mismatch', 'Mismatched anonymous DEFINE() module: ' + + args[args.length - 1])); + } else { + //args are id, deps, factory. Should be normalized by the + //DEFINE() function. + callGetModule(args); + } + } + context.defQueueMap = {}; + } + + context = { + config: config, + contextName: contextName, + registry: registry, + defined: defined, + urlFetched: urlFetched, + defQueue: defQueue, + defQueueMap: {}, + Module: Module, + makeModuleMap: makeModuleMap, + nextTick: req.nextTick, + onError: onError, + + /** + * Set a configuration for the context. + * @param {Object} cfg config object to integrate. + */ + configure: function (cfg) { + //Make sure the baseUrl ends in a slash. + if (cfg.baseUrl) { + if (cfg.baseUrl.charAt(cfg.baseUrl.length - 1) !== '/') { + cfg.baseUrl += '/'; + } + } + + // Convert old style urlArgs string to a function. + if (typeof cfg.urlArgs === 'string') { + var urlArgs = cfg.urlArgs; + cfg.urlArgs = function(id, url) { + return (url.indexOf('?') === -1 ? '?' : '&') + urlArgs; + }; + } + + //Save off the paths since they REQUIRE special processing, + //they are additive. + var shim = config.shim, + objs = { + paths: true, + bundles: true, + config: true, + map: true + }; + + eachProp(cfg, function (value, prop) { + if (objs[prop]) { + if (!config[prop]) { + config[prop] = {}; + } + mixin(config[prop], value, true, true); + } else { + config[prop] = value; + } + }); + + //Reverse map the bundles + if (cfg.bundles) { + eachProp(cfg.bundles, function (value, prop) { + each(value, function (v) { + if (v !== prop) { + bundlesMap[v] = prop; + } + }); + }); + } + + //Merge shim + if (cfg.shim) { + eachProp(cfg.shim, function (value, id) { + //Normalize the structure + if (isArray(value)) { + value = { + deps: value + }; + } + if ((value.exports || value.init) && !value.exportsFn) { + value.exportsFn = context.makeShimExports(value); + } + shim[id] = value; + }); + config.shim = shim; + } + + //Adjust packages if necessary. + if (cfg.packages) { + each(cfg.packages, function (pkgObj) { + var location, name; + + pkgObj = typeof pkgObj === 'string' ? {name: pkgObj} : pkgObj; + + name = pkgObj.name; + location = pkgObj.location; + if (location) { + config.paths[name] = pkgObj.location; + } + + //Save pointer to main module ID for pkg name. + //Remove leading dot in main, so main paths are normalized, + //and remove any trailing .js, since different package + //envs have different conventions: some use a module name, + //some use a file name. + config.pkgs[name] = pkgObj.name + '/' + (pkgObj.main || 'main') + .replace(currDirRegExp, '') + .replace(jsSuffixRegExp, ''); + }); + } + + //If there are any "waiting to execute" modules in the registry, + //update the maps for them, since their info, like URLs to load, + //may have changed. + eachProp(registry, function (mod, id) { + //If module already has init called, since it is too + //late to modify them, and ignore unnormalized ones + //since they are transient. + if (!mod.inited && !mod.map.unnormalized) { + mod.map = makeModuleMap(id, null, true); + } + }); + + //If a deps array or a config callback is specified, then call + //REQUIRE with those args. This is useful when REQUIRE is defined as a + //config object before REQUIRE.js is loaded. + if (cfg.deps || cfg.callback) { + context.REQUIRE(cfg.deps || [], cfg.callback); + } + }, + + makeShimExports: function (value) { + function fn() { + var ret; + if (value.init) { + ret = value.init.apply(global, arguments); + } + return ret || (value.exports && getGlobal(value.exports)); + } + return fn; + }, + + makeRequire: function (relMap, options) { + options = options || {}; + + function localRequire(deps, callback, errback) { + var id, map, requireMod; + + if (options.enableBuildCallback && callback && isFunction(callback)) { + callback.__requireJsBuild = true; + } + + if (typeof deps === 'string') { + if (isFunction(callback)) { + //Invalid call + return onError(makeError('requireargs', 'Invalid REQUIRE call'), errback); + } + + //If REQUIRE|exports|module are requested, get the + //value for them from the special handlers. Caveat: + //this only works while module is being defined. + if (relMap && hasProp(handlers, deps)) { + return handlers[deps](registry[relMap.id]); + } + + //Synchronous access to one module. If REQUIRE.get is + //available (as in the Node adapter), prefer that. + if (req.get) { + return req.get(context, deps, relMap, localRequire); + } + + //Normalize module name, if it contains . or .. + map = makeModuleMap(deps, relMap, false, true); + id = map.id; + + if (!hasProp(defined, id)) { + return onError(makeError('notloaded', 'Module name "' + + id + + '" has not been loaded yet for context: ' + + contextName + + (relMap ? '' : '. Use REQUIRE([])'))); + } + return defined[id]; + } + + //Grab defines waiting in the global queue. + intakeDefines(); + + //Mark all the dependencies as needing to be loaded. + context.nextTick(function () { + //Some defines could have been added since the + //REQUIRE call, collect them. + intakeDefines(); + + requireMod = getModule(makeModuleMap(null, relMap)); + + //Store if map config should be applied to this REQUIRE + //call for dependencies. + requireMod.skipMap = options.skipMap; + + requireMod.init(deps, callback, errback, { + enabled: true + }); + + checkLoaded(); + }); + + return localRequire; + } + + mixin(localRequire, { + isBrowser: isBrowser, + + /** + * Converts a module name + .extension into an URL path. + * *Requires* the use of a module name. It does not support using + * plain URLs like nameToUrl. + */ + toUrl: function (moduleNamePlusExt) { + var ext, + index = moduleNamePlusExt.lastIndexOf('.'), + segment = moduleNamePlusExt.split('/')[0], + isRelative = segment === '.' || segment === '..'; + + //Have a file extension alias, and it is not the + //dots from a relative path. + if (index !== -1 && (!isRelative || index > 1)) { + ext = moduleNamePlusExt.substring(index, moduleNamePlusExt.length); + moduleNamePlusExt = moduleNamePlusExt.substring(0, index); + } + + return context.nameToUrl(normalize(moduleNamePlusExt, + relMap && relMap.id, true), ext, true); + }, + + defined: function (id) { + return hasProp(defined, makeModuleMap(id, relMap, false, true).id); + }, + + specified: function (id) { + id = makeModuleMap(id, relMap, false, true).id; + return hasProp(defined, id) || hasProp(registry, id); + } + }); + + //Only allow undef on top level REQUIRE calls + if (!relMap) { + localRequire.undef = function (id) { + //Bind any waiting DEFINE() calls to this context, + //fix for #408 + takeGlobalQueue(); + + var map = makeModuleMap(id, relMap, true), + mod = getOwn(registry, id); + + mod.undefed = true; + removeScript(id); + + delete defined[id]; + delete urlFetched[map.url]; + delete undefEvents[id]; + + //Clean queued defines too. Go backwards + //in array so that the splices do not + //mess up the iteration. + eachReverse(defQueue, function(args, i) { + if (args[0] === id) { + defQueue.splice(i, 1); + } + }); + delete context.defQueueMap[id]; + + if (mod) { + //Hold on to listeners in case the + //module will be attempted to be reloaded + //using a different config. + if (mod.events.defined) { + undefEvents[id] = mod.events; + } + + cleanRegistry(id); + } + }; + } + + return localRequire; + }, + + /** + * Called to enable a module if it is still in the registry + * awaiting enablement. A second arg, parent, the parent module, + * is passed in for context, when this method is overridden by + * the optimizer. Not shown here to keep code compact. + */ + enable: function (depMap) { + var mod = getOwn(registry, depMap.id); + if (mod) { + getModule(depMap).enable(); + } + }, + + /** + * Internal method used by environment adapters to complete a load event. + * A load event could be a script load or just a load pass from a synchronous + * load call. + * @param {String} moduleName the name of the module to potentially complete. + */ + completeLoad: function (moduleName) { + var found, args, mod, + shim = getOwn(config.shim, moduleName) || {}, + shExports = shim.exports; + + takeGlobalQueue(); + + while (defQueue.length) { + args = defQueue.shift(); + if (args[0] === null) { + args[0] = moduleName; + //If already found an anonymous module and bound it + //to this name, then this is some other anon module + //waiting for its completeLoad to fire. + if (found) { + break; + } + found = true; + } else if (args[0] === moduleName) { + //Found matching DEFINE call for this script! + found = true; + } + + callGetModule(args); + } + context.defQueueMap = {}; + + //Do this after the cycle of callGetModule in case the result + //of those calls/init calls changes the registry. + mod = getOwn(registry, moduleName); + + if (!found && !hasProp(defined, moduleName) && mod && !mod.inited) { + if (config.enforceDefine && (!shExports || !getGlobal(shExports))) { + if (hasPathFallback(moduleName)) { + return; + } else { + return onError(makeError('nodefine', + 'No DEFINE call for ' + moduleName, + null, + [moduleName])); + } + } else { + //A script that does not call DEFINE(), so just simulate + //the call for it. + callGetModule([moduleName, (shim.deps || []), shim.exportsFn]); + } + } + + checkLoaded(); + }, + + /** + * Converts a module name to a file path. Supports cases where + * moduleName may actually be just an URL. + * Note that it **does not** call normalize on the moduleName, + * it is assumed to have already been normalized. This is an + * internal API, not a public one. Use toUrl for the public API. + */ + nameToUrl: function (moduleName, ext, skipExt) { + var paths, syms, i, parentModule, url, + parentPath, bundleId, + pkgMain = getOwn(config.pkgs, moduleName); + + if (pkgMain) { + moduleName = pkgMain; + } + + bundleId = getOwn(bundlesMap, moduleName); + + if (bundleId) { + return context.nameToUrl(bundleId, ext, skipExt); + } + + //If a colon is in the URL, it indicates a protocol is used and it is just + //an URL to a file, or if it starts with a slash, contains a query arg (i.e. ?) + //or ends with .js, then assume the user meant to use an url and not a module id. + //The slash is important for protocol-less URLs as well as full paths. + if (req.jsExtRegExp.test(moduleName)) { + //Just a plain path, not module name lookup, so just return it. + //Add extension if it is included. This is a bit wonky, only non-.js things pass + //an extension, this method probably needs to be reworked. + url = moduleName + (ext || ''); + } else { + //A module that needs to be converted to a path. + paths = config.paths; + + syms = moduleName.split('/'); + //For each module name segment, see if there is a path + //registered for it. Start with most specific name + //and work up from it. + for (i = syms.length; i > 0; i -= 1) { + parentModule = syms.slice(0, i).join('/'); + + parentPath = getOwn(paths, parentModule); + if (parentPath) { + //If an array, it means there are a few choices, + //Choose the one that is desired + if (isArray(parentPath)) { + parentPath = parentPath[0]; + } + syms.splice(0, i, parentPath); + break; + } + } + + //Join the path parts together, then figure out if baseUrl is needed. + url = syms.join('/'); + url += (ext || (/^data\:|^blob\:|\?/.test(url) || skipExt ? '' : '.js')); + url = (url.charAt(0) === '/' || url.match(/^[\w\+\.\-]+:/) ? '' : config.baseUrl) + url; + } + + return config.urlArgs && !/^blob\:/.test(url) ? + url + config.urlArgs(moduleName, url) : url; + }, + + //Delegates to req.load. Broken out as a separate function to + //allow overriding in the optimizer. + load: function (id, url) { + req.load(context, id, url); + }, + + /** + * Executes a module callback function. Broken out as a separate function + * solely to allow the build system to sequence the files in the built + * layer in the right sequence. + * + * @private + */ + execCb: function (name, callback, args, exports) { + return callback.apply(exports, args); + }, + + /** + * callback for script loads, used to check status of loading. + * + * @param {Event} evt the event from the browser for the script + * that was loaded. + */ + onScriptLoad: function (evt) { + //Using currentTarget instead of target for Firefox 2.0's sake. Not + //all old browsers will be supported, but this one was easy enough + //to support and still makes sense. + if (evt.type === 'load' || + (readyRegExp.test((evt.currentTarget || evt.srcElement).readyState))) { + //Reset interactive script so a script node is not held onto for + //to long. + interactiveScript = null; + + //Pull out the name of the module and the context. + var data = getScriptData(evt); + context.completeLoad(data.id); + } + }, + + /** + * Callback for script errors. + */ + onScriptError: function (evt) { + var data = getScriptData(evt); + if (!hasPathFallback(data.id)) { + var parents = []; + eachProp(registry, function(value, key) { + if (key.indexOf('_@r') !== 0) { + each(value.depMaps, function(depMap) { + if (depMap.id === data.id) { + parents.push(key); + return true; + } + }); + } + }); + return onError(makeError('scripterror', 'Script error for "' + data.id + + (parents.length ? + '", needed by: ' + parents.join(', ') : + '"'), evt, [data.id])); + } + } + }; + + context.REQUIRE = context.makeRequire(); + return context; + } + + /** + * Main entry point. + * + * If the only argument to REQUIRE is a string, then the module that + * is represented by that string is fetched for the appropriate context. + * + * If the first argument is an array, then it will be treated as an array + * of dependency string names to fetch. An optional function callback can + * be specified to execute when all of those dependencies are available. + * + * Make a local req variable to help Caja compliance (it assumes things + * on a REQUIRE that are not standardized), and to give a short + * name for minification/local scope use. + */ + req = REQUIREJS = function (deps, callback, errback, optional) { + + //Find the right context, use default + var context, config, + contextName = defContextName; + + // Determine if have config object in the call. + if (!isArray(deps) && typeof deps !== 'string') { + // deps is a config object + config = deps; + if (isArray(callback)) { + // Adjust args if there are dependencies + deps = callback; + callback = errback; + errback = optional; + } else { + deps = []; + } + } + + if (config && config.context) { + contextName = config.context; + } + + context = getOwn(contexts, contextName); + if (!context) { + context = contexts[contextName] = req.s.newContext(contextName); + } + + if (config) { + context.configure(config); + } + + return context.REQUIRE(deps, callback, errback); + }; + + /** + * Support REQUIRE.config() to make it easier to cooperate with other + * AMD loaders on globally agreed names. + */ + req.config = function (config) { + return req(config); + }; + + /** + * Execute something after the current tick + * of the event loop. Override for other envs + * that have a better solution than setTimeout. + * @param {Function} fn function to execute later. + */ + req.nextTick = typeof setTimeout !== 'undefined' ? function (fn) { + setTimeout(fn, 4); + } : function (fn) { fn(); }; + + /** + * Export REQUIRE as a global, but only if it does not already exist. + */ + if (!REQUIRE) { + REQUIRE = req; + } + + req.version = version; + + //Used to filter out dependencies that are already paths. + req.jsExtRegExp = /^\/|:|\?|\.js$/; + req.isBrowser = isBrowser; + s = req.s = { + contexts: contexts, + newContext: newContext + }; + + //Create default context. + req({}); + + //Exports some context-sensitive methods on global REQUIRE. + each([ + 'toUrl', + 'undef', + 'defined', + 'specified' + ], function (prop) { + //Reference from contexts instead of early binding to default context, + //so that during builds, the latest instance of the default context + //with its config gets used. + req[prop] = function () { + var ctx = contexts[defContextName]; + return ctx.REQUIRE[prop].apply(ctx, arguments); + }; + }); + + if (isBrowser) { + head = s.head = document.getElementsByTagName('head')[0]; + //If BASE tag is in play, using appendChild is a problem for IE6. + //When that browser dies, this can be removed. Details in this jQuery bug: + //http://dev.jquery.com/ticket/2709 + baseElement = document.getElementsByTagName('base')[0]; + if (baseElement) { + head = s.head = baseElement.parentNode; + } + } + + /** + * Any errors that REQUIRE explicitly generates will be passed to this + * function. Intercept/override it if you want custom error handling. + * @param {Error} err the error object. + */ + req.onError = defaultOnError; + + /** + * Creates the node for the load command. Only used in browser envs. + */ + req.createNode = function (config, moduleName, url) { + var node = config.xhtml ? + document.createElementNS('http://www.w3.org/1999/xhtml', 'html:script') : + document.createElement('script'); + node.type = config.scriptType || 'text/javascript'; + node.charset = 'utf-8'; + node.async = true; + return node; + }; + + /** + * Does the request to load a module for the browser case. + * Make this a separate function to allow other environments + * to override it. + * + * @param {Object} context the REQUIRE context to find state. + * @param {String} moduleName the name of the module. + * @param {Object} url the URL to the module. + */ + req.load = function (context, moduleName, url) { + var config = (context && context.config) || {}, + node; + if (isBrowser) { + //In the browser so use a script tag + node = req.createNode(config, moduleName, url); + + node.setAttribute('data-requirecontext', context.contextName); + node.setAttribute('data-requiremodule', moduleName); + + //Set up load listener. Test attachEvent first because IE9 has + //a subtle issue in its addEventListener and script onload firings + //that do not match the behavior of all other browsers with + //addEventListener support, which fire the onload event for a + //script right after the script execution. See: + //https://connect.microsoft.com/IE/feedback/details/648057/script-onload-event-is-not-fired-immediately-after-script-execution + //UNFORTUNATELY Opera implements attachEvent but does not follow the script + //script execution mode. + if (node.attachEvent && + //Check if node.attachEvent is artificially added by custom script or + //natively supported by browser + //read https://github.com/REQUIREJS/REQUIREJS/issues/187 + //if we can NOT find [native code] then it must NOT natively supported. + //in IE8, node.attachEvent does not have toString() + //Note the test for "[native code" with no closing brace, see: + //https://github.com/REQUIREJS/REQUIREJS/issues/273 + !(node.attachEvent.toString && node.attachEvent.toString().indexOf('[native code') < 0) && + !isOpera) { + //Probably IE. IE (at least 6-8) do not fire + //script onload right after executing the script, so + //we cannot tie the anonymous DEFINE call to a name. + //However, IE reports the script as being in 'interactive' + //readyState at the time of the DEFINE call. + useInteractive = true; + + node.attachEvent('onreadystatechange', context.onScriptLoad); + //It would be great to add an error handler here to catch + //404s in IE9+. However, onreadystatechange will fire before + //the error handler, so that does not help. If addEventListener + //is used, then IE will fire error before load, but we cannot + //use that pathway given the connect.microsoft.com issue + //mentioned above about not doing the 'script execute, + //then fire the script load event listener before execute + //next script' that other browsers do. + //Best hope: IE10 fixes the issues, + //and then destroys all installs of IE 6-9. + //node.attachEvent('onerror', context.onScriptError); + } else { + node.addEventListener('load', context.onScriptLoad, false); + node.addEventListener('error', context.onScriptError, false); + } + node.src = url; + + //Calling onNodeCreated after all properties on the node have been + //set, but before it is placed in the DOM. + if (config.onNodeCreated) { + config.onNodeCreated(node, config, moduleName, url); + } + + //For some cache cases in IE 6-8, the script executes before the end + //of the appendChild execution, so to tie an anonymous DEFINE + //call to the module name (which is stored on the node), hold on + //to a reference to this node, but clear after the DOM insertion. + currentlyAddingScript = node; + if (baseElement) { + head.insertBefore(node, baseElement); + } else { + head.appendChild(node); + } + currentlyAddingScript = null; + + return node; + } else if (isWebWorker) { + try { + //In a web worker, use importScripts. This is not a very + //efficient use of importScripts, importScripts will block until + //its script is downloaded and evaluated. However, if web workers + //are in play, the expectation is that a build has been done so + //that only one script needs to be loaded anyway. This may need + //to be reevaluated if other use cases become common. + + // Post a task to the event loop to work around a bug in WebKit + // where the worker gets garbage-collected after calling + // importScripts(): https://webkit.org/b/153317 + setTimeout(function() {}, 0); + importScripts(url); + + //Account for anonymous modules + context.completeLoad(moduleName); + } catch (e) { + context.onError(makeError('importscripts', + 'importScripts failed for ' + + moduleName + ' at ' + url, + e, + [moduleName])); + } + } + }; + + function getInteractiveScript() { + if (interactiveScript && interactiveScript.readyState === 'interactive') { + return interactiveScript; + } + + eachReverse(scripts(), function (script) { + if (script.readyState === 'interactive') { + return (interactiveScript = script); + } + }); + return interactiveScript; + } + + //Look for a data-main script attribute, which could also adjust the baseUrl. + if (isBrowser && !cfg.skipDataMain) { + //Figure out baseUrl. Get it from the script tag with REQUIRE.js in it. + eachReverse(scripts(), function (script) { + //Set the 'head' where we can append children by + //using the script's parent. + if (!head) { + head = script.parentNode; + } + + //Look for a data-main attribute to set main script for the page + //to load. If it is there, the path to data main becomes the + //baseUrl, if it is not already set. + dataMain = script.getAttribute('data-main'); + if (dataMain) { + //Preserve dataMain in case it is a path (i.e. contains '?') + mainScript = dataMain; + + //Set final baseUrl if there is not already an explicit one, + //but only do so if the data-main value is not a loader plugin + //module ID. + if (!cfg.baseUrl && mainScript.indexOf('!') === -1) { + //Pull off the directory of data-main for use as the + //baseUrl. + src = mainScript.split('/'); + mainScript = src.pop(); + subPath = src.length ? src.join('/') + '/' : './'; + + cfg.baseUrl = subPath; + } + + //Strip off any trailing .js since mainScript is now + //like a module name. + mainScript = mainScript.replace(jsSuffixRegExp, ''); + + //If mainScript is still a path, fall back to dataMain + if (req.jsExtRegExp.test(mainScript)) { + mainScript = dataMain; + } + + //Put the data-main script in the files to load. + cfg.deps = cfg.deps ? cfg.deps.concat(mainScript) : [mainScript]; + + return true; + } + }); + } + + /** + * The function that handles definitions of modules. Differs from + * REQUIRE() in that a string for the module should be the first argument, + * and the function to execute after dependencies are loaded should + * return a value to DEFINE the module corresponding to the first argument's + * name. + */ + DEFINE = function (name, deps, callback) { + var node, context; + + //Allow for anonymous modules + if (typeof name !== 'string') { + //Adjust args appropriately + callback = deps; + deps = name; + name = null; + } + + //This module may not have dependencies + if (!isArray(deps)) { + callback = deps; + deps = null; + } + + //If no name, and callback is a function, then figure out if it a + //CommonJS thing with dependencies. + if (!deps && isFunction(callback)) { + deps = []; + //Remove comments from the callback string, + //look for REQUIRE calls, and pull them into the dependencies, + //but only if there are function args. + if (callback.length) { + callback + .toString() + .replace(commentRegExp, commentReplace) + .replace(cjsRequireRegExp, function (match, dep) { + deps.push(dep); + }); + + //May be a CommonJS thing even without REQUIRE calls, but still + //could use exports, and module. Avoid doing exports and module + //work though if it just needs REQUIRE. + //REQUIRES the function to expect the CommonJS variables in the + //order listed below. + deps = (callback.length === 1 ? ['REQUIRE'] : ['REQUIRE', 'exports', 'module']).concat(deps); + } + } + + //If in IE 6-8 and hit an anonymous DEFINE() call, do the interactive + //work. + if (useInteractive) { + node = currentlyAddingScript || getInteractiveScript(); + if (node) { + if (!name) { + name = node.getAttribute('data-requiremodule'); + } + context = contexts[node.getAttribute('data-requirecontext')]; + } + } + + //Always save off evaluating the def call until the script onload handler. + //This allows multiple modules to be in a file without prematurely + //tracing dependencies, and allows for anonymous module support, + //where the module name is not known until the script onload event + //occurs. If no context, use the global queue, and get it processed + //in the onscript load callback. + if (context) { + context.defQueue.push([name, deps, callback]); + context.defQueueMap[name] = true; + } else { + globalDefQueue.push([name, deps, callback]); + } + }; + + DEFINE.amd = { + jQuery: true + }; + + /** + * Executes the text. Normally just uses eval, but can be modified + * to use a better, environment-specific call. Only used for transpiling + * loader plugins, not for plain JS modules. + * @param {String} text the text to execute/evaluate. + */ + req.exec = function (text) { + /*jslint evil: true */ + return eval(text); + }; + + //Set up with config info. + req(cfg); +}(this, (typeof setTimeout === 'undefined' ? undefined : setTimeout))); diff --git a/Server/BotsScripts.js b/Server/BotsScripts.js new file mode 100644 index 000000000..1f58ade6d --- /dev/null +++ b/Server/BotsScripts.js @@ -0,0 +1,78 @@ +exports.newBotScripts = function newBotScripts() { + + let thisObject = { + getScript: getScript, + initialize: initialize + } + + const STORAGE = require('../Server/Storage'); + let storage = STORAGE.newStorage(); + + let serverConfig; + let storageData; + + return thisObject; + + function initialize(pServerConfig, callBackFunction) { + + let storageData = new Map(); // For backwards compatibility only. Not used. + + storage.initialize(storageData, pServerConfig); + + callBackFunction(); + } + + function getScript(pDevTeam, pSource, pRepo, pPath, callBackFunction) { + + storage.readData(pDevTeam + "/" + pSource, pRepo, pPath, false, onDataArrived); + + function onDataArrived(err, pData) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] BotScripts -> retrieveScripts -> Cloud -> onDataArrived -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] BotScripts -> retrieveScripts -> Cloud -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] BotScripts -> retrieveScripts -> Cloud -> onDataArrived -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let data; + + if (pData !== undefined) { + + data = pData.toString(); + data = data.trim(); // remove first byte with some encoding. + + data = browserify(data); + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE, data); + + } + catch (err) { + console.log("[ERROR] BotScripts -> retrieveScripts -> Cloud -> onDataArrived -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function browserify(pData) { + + let result = pData.substring(pData.indexOf('=') + 2); + + result = replaceAll(result, "require", "webRequire"); + result = replaceAll(result, "global", "window"); + + return result; + + function replaceAll(text, search, replacement) { + return text.replace(new RegExp(search, 'g'), replacement); + } + } +} \ No newline at end of file diff --git a/Server/CloudScripts.js b/Server/CloudScripts.js new file mode 100644 index 000000000..5df914a13 --- /dev/null +++ b/Server/CloudScripts.js @@ -0,0 +1,172 @@ +exports.newCloudScripts = function newCloudScripts() { + + let thisObject = { + loadCloudScripts: loadCloudScripts, + initialize: initialize + } + + const GITHUB = require('../Server/Github'); + let github = GITHUB.newGithub(); + + const STORAGE = require('../Server/Storage'); + let storage = STORAGE.newStorage(); + + let serverConfig; + let storageData; + let ecosystem; + let ecosystemObject; + + return thisObject; + + function initialize(pEcosystem, pEcosystemObject, pServerConfig, pStorageData, callBackFunction) { + + serverConfig = pServerConfig; + storageData = pStorageData; + ecosystem = pEcosystem; + ecosystemObject = pEcosystemObject; + + storage.initialize(storageData, serverConfig); + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + + function loadCloudScripts(callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] CloudScripts -> loadCloudScripts -> Entering function."); } + + /* + + First we will get a list of the scripts to be loaded, and then we will load them into the cache. + + */ + + storage.readData('AdvancedAlgos', 'AACloud', 'web.config.json', true, onDataArrived); + + function onDataArrived(err, pData) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] CloudScripts -> loadCloudScripts -> Cloud -> onDataArrived -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] CloudScripts -> loadCloudScripts -> Cloud -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] CloudScripts -> loadCloudScripts -> Cloud -> onDataArrived -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let data = pData.toString(); + data = data.trim(); // remove first byte with some encoding. + + dataObject = JSON.parse(data); + retrieveScripts(dataObject, callBackFunction); + } + catch (err) { + console.log("[ERROR] CloudScripts -> loadCloudScripts -> Cloud -> onDataArrived -> Error = " + err); + } + } + } + catch (err) { + console.log("[ERROR] CloudScripts -> loadCloudScripts -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function retrieveScripts(pCloudWebConfig, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] CloudScripts -> retrieveScripts -> Entering function."); } + + /* + + Here we go through the webModules Array and bring them all. + + */ + + let modulesRetrieved = 0; + + for (let i = 0; i < pCloudWebConfig.webModules.length; i++) { + + let webModule = pCloudWebConfig.webModules[i].name; + + storage.readData('AdvancedAlgos', 'AACloud', webModule, true, onDataArrived); + + function onDataArrived(err, pData) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] CloudScripts -> retrieveScripts -> Cloud -> onDataArrived -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] CloudScripts -> retrieveScripts -> Cloud -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] CloudScripts -> retrieveScripts -> Cloud -> onDataArrived -> err.message = " + err.message); + console.log("[ERROR] CloudScripts -> retrieveScripts -> Cloud -> onDataArrived -> ewebModule = " + webModule); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let data = pData.toString(); + data = data.trim(); // remove first byte with some encoding. + + data = browserify(data); + + storageData.set(webModule, data); + + modulesRetrieved++; + if (modulesRetrieved === pCloudWebConfig.webModules.length) { + createJS(callBackFunction); + } + + } + catch (err) { + console.log("[ERROR] CloudScripts -> retrieveScripts -> Cloud -> onDataArrived -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function browserify(pData) { + + let result = pData.substring(pData.indexOf('=') + 2); + + result = replaceAll(result, "require", "webRequire"); + result = replaceAll(result, "global", "window"); + + return result; + + function replaceAll(text, search, replacement) { + return text.replace(new RegExp(search, 'g'), replacement); + } + } + + function createJS(callBackFunction) { + + let lines = ""; + let jsLine = '' + '\n' + ' "AACloud/@module@",' + + for (let i = 0; i < pCloudWebConfig.webModules.length; i++) { + + let webModule = pCloudWebConfig.webModules[i].name; + let htmlLineCopy = jsLine; + let newLink = htmlLineCopy.replace('@module@', webModule); + + lines = lines + newLink; + } + + callBackFunction(global.DEFAULT_OK_RESPONSE, lines); + } + + } + catch (err) { + console.log("[ERROR] CloudScripts -> retrieveScripts -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +} \ No newline at end of file diff --git a/Server/ConfigReader.js b/Server/ConfigReader.js new file mode 100644 index 000000000..149506cd8 --- /dev/null +++ b/Server/ConfigReader.js @@ -0,0 +1,621 @@ +exports.newConfigReader = function newConfigReader() { + + let thisObject = { + loadConfigs: loadConfigs, + initialize: initialize + } + + const GITHUB = require('../Server/Github'); + let github = GITHUB.newGithub(); + + const STORAGE = require('../Server/Storage'); + let storage = STORAGE.newStorage(); + + let serverConfig; + let storageData; + let ecosystem; + let ecosystemObject; + + return thisObject; + + function initialize(pEcosystem, pEcosystemObject, pStorageData, callBackFunction) { + + storageData = pStorageData; + ecosystem = pEcosystem; + ecosystemObject = pEcosystemObject; + + readAAWebConfig(); + + function readAAWebConfig() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readAAWebConfig -> Entering function."); } + + let fs = require('fs'); + + let fileName = './this.server.config.json'; + fs.readFile(fileName, onFileRead); + + function onFileRead(err, file) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readAAWebConfig -> onFileRead -> Entering function."); } + + let fileText; + + fileText = file.toString(); + fileText = fileText.trim(); // remove first byte with some encoding. + + serverConfig = JSON.parse(fileText); + + if (LOG_FILE_CONTENT === true) { console.log("[INFO] ConfigReader -> readAAWebConfig -> onFileRead -> fileText = " + fileText); } + + CONSOLE_LOG = serverConfig.webServerLog.console; + LOG_FILE_CONTENT = serverConfig.webServerLog.fileContent; + + /* Finalize initializations. */ + + storage.initialize(storageData, serverConfig); + + callBackFunction(global.DEFAULT_OK_RESPONSE, serverConfig); + } + catch (err) { + console.log("[ERROR] ConfigReader -> readAAWebConfig -> onFileRead -> File = " + fileName + " Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + + } + } + catch (err) { + console.log("[ERROR] ConfigReader -> readAAWebConfig -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function loadConfigs(callBackFunction) { + + readEcosystemConfig(); + + function readEcosystemConfig() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readEcosystemConfig -> Entering function."); } + + /* + + This configuration file is the backbone of the system. The first file we are going to get is a template where other configurations are + injected and the files ends up inflated with all these configs in one single JSON object that in turn is later injected into a + javascript module with an object that is going to instantiate it at run-time. + + */ + + storage.readData('AdvancedAlgos', 'AAPlatform', 'ecosystem.json', true, onDataArrived); + + function onDataArrived(err, pData) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readEcosystemConfig -> Cloud -> onDataArrived -> Entering function."); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] ConfigReader -> readEcosystemConfig -> Cloud -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] ConfigReader -> readEcosystemConfig -> Cloud -> onDataArrived -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + ecosystem = pData.toString(); + ecosystem = ecosystem.trim(); // remove first byte with some encoding. + + ecosystemObject = JSON.parse(ecosystem); + readHostsConfigs(); + } + catch (err) { + console.log("[ERROR] ConfigReader -> loadConfigs -> readEcosystemConfig -> onDataArrived -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + catch (err) { + console.log("[ERROR] ConfigReader -> loadConfigs -> readEcosystemConfig -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function readHostsConfigs() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> Entering function."); } + + let requestsSent = 0; + let responsesReceived = 0; + + for (let i = 0; i < ecosystemObject.hosts.length; i++) { + + let host = ecosystemObject.hosts[i]; + + getCompetitions(); + getPlotters(); + + function getCompetitions() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getCompetitions -> Entering function."); } + + /* In this first section we will load the competitions configurations. */ + + for (let j = 0; j < host.competitions.length; j++) { + + let competition = host.competitions[j]; + + requestsSent++; + + storage.readData(host.codeName + "/" + "competitions", competition.repo, competition.configFile, true, onDataArrived); + + function onDataArrived(err, pData) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getCompetitions -> Cloud -> onDataArrived -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getCompetitions -> Cloud -> onDataArrived -> host.codeName = " + host.codeName); } + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getCompetitions -> Cloud -> onDataArrived -> competition.repo = " + competition.repo); } + if (LOG_FILE_CONTENT === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getCompetitions -> Cloud -> onDataArrived -> pData = " + pData); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] ConfigReader -> readHostsConfigs -> getCompetitions -> Cloud -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] ConfigReader -> readHostsConfigs -> getCompetitions -> Cloud -> onDataArrived -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + responsesReceived++; + + pData = pData.toString(); + pData = pData.trim(); // remove first byte with some encoding. + + let configObj = JSON.parse(pData); + + /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ + + configObj.repo = competition.repo; + configObj.configFile = competition.configFile; + + host.competitions[j] = configObj; + + if (requestsSent === responsesReceived) { + + readDevTeamsConfigs(); + + } + + } + catch (err) { + console.log("[ERROR] ConfigReader -> readHostsConfigs -> getCompetitions -> Cloud -> onDataArrived -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + } + catch (err) { + console.log("[ERROR] ConfigReader -> readHostsConfigs -> getCompetitions -> getCompetitions -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getPlotters() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getPlotters -> getPlotters -> Entering function."); } + + /* In this second section we will load the competition plotters configurations. */ + + for (let j = 0; j < host.plotters.length; j++) { + + let plotter = host.plotters[j]; + + requestsSent++; + + storage.readData(host.codeName + "/" + "plotters", plotter.repo, plotter.configFile, true, onDataArrived); + + function onDataArrived(err, pData) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getPlotters -> Cloud -> onDataArrived -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getPlotters -> Cloud -> onDataArrived -> host.codeName = " + host.codeName); } + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getPlotters -> Cloud -> onDataArrived -> plotter.repo = " + plotter.repo); } + if (LOG_FILE_CONTENT === true) { console.log("[INFO] ConfigReader -> readHostsConfigs -> getPlotters -> Cloud -> onDataArrived -> pData = " + pData); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] ConfigReader -> readHostsConfigs -> getPlotters -> Cloud -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] ConfigReader -> readHostsConfigs -> getPlotters -> Cloud -> onDataArrived -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + responsesReceived++; + + pData = pData.toString(); + pData = pData.trim(); // remove first byte with some encoding. + + let configObj = JSON.parse(pData); + + /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ + + configObj.repo = plotter.repo; + configObj.configFile = plotter.configFile; + + host.plotters[j] = configObj; + + if (requestsSent === responsesReceived) { + + readDevTeamsConfigs(); + + } + + } + catch (err) { + console.log("[ERROR] ConfigReader -> readHostsConfigs -> getPlotters -> Cloud -> onDataArrived -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + } + catch (err) { + console.log("[ERROR] ConfigReader -> readHostsConfigs -> getPlotters -> getCompetitions -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + } + catch (err) { + console.log("[ERROR] ConfigReader -> readHostsConfigs -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function readDevTeamsConfigs() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> Entering function."); } + + /* + + Each bot has its configuration at its own repo since each team must be able to change it at will. + So what we do here is to use the master config at the AAPlatform repo that we already have on + memory and inject into it the config of each bot. + + Inmediatelly after that, we also load the Plotters configs using the same technique. + + */ + + let requestsSent = 0; + let responsesReceived = 0; + + for (let i = 0; i < ecosystemObject.devTeams.length; i++) { + + let devTeam = ecosystemObject.devTeams[i]; + + getBots(); + getPlotters(); + + function getBots() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getBots -> Entering function."); } + + /* In the next section we are loading the bots configurations. */ + + for (let j = 0; j < devTeam.bots.length; j++) { + + let bot = devTeam.bots[j]; + + requestsSent++; + + storage.readData(devTeam.codeName + "/" + "bots", bot.repo, bot.configFile, true, onDataArrived); + + function onDataArrived(err, pData) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getBots -> Cloud -> onDataArrived -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getBots -> Cloud -> onDataArrived -> devTeam.codeName = " + devTeam.codeName); } + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getBots -> Cloud -> onDataArrived -> bot.repo = " + bot.repo); } + if (LOG_FILE_CONTENT === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getBots -> Cloud -> onDataArrived -> pData = " + pData); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> getBots -> Cloud -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> getBots -> Cloud -> onDataArrived -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + responsesReceived++; + + if (pData !== "{}") { // If for any reason we could not tet this config, then we just exclude this bot to improve resiliency. + + pData = pData.toString(); + pData = pData.trim(); // remove first byte with some encoding. + + let configObj = JSON.parse(pData); + + /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ + + configObj.repo = bot.repo; + configObj.configFile = bot.configFile; + + addStoragePermissions(configObj); + + /* Here we will add all the possible Clones of this bot */ + + if (configObj.genes !== undefined) { + + /* Here, based on the genes we will create one instance for each combination of genes */ + + let valueMatrix = []; + + for (let i = 0; i < configObj.genes.length; i++) { + + let gene = configObj.genes[i]; + + let possibleValues = []; + + for (let j = gene.lowerLimit; j <= gene.upperLimit; j++) { + + possibleValues.push(j); + + } + + valueMatrix.push(possibleValues); + + } + + /* + Now we have all the possible genes values in a multi-dimensional matrix. + We will go thorugh each of the elements of each dimension and get each combination possible with the other dimmensions. + */ + + let combinations = []; + let combination = []; + let dimensionIndex = 0; + + calculateCombinations(dimensionIndex, combination); + + function calculateCombinations(pDimensionIndex, pCombination) { + + let dimension = valueMatrix[pDimensionIndex]; + + for (let j = 0; j < dimension.length; j++) { + + let copy = JSON.parse(JSON.stringify(pCombination)); + + let value = dimension[j]; + copy.push(value); + + if (pDimensionIndex < valueMatrix.length - 1) { + + calculateCombinations(pDimensionIndex + 1, copy); + + } else { + + combinations.push(copy); + + } + } + } + + /* We delete the bot definition since it is going to be replaced by one definition for each of its clones. */ + + devTeam.bots.splice(j); + + /* At this point we have an array with all the possible combinations, we just need to create an instance for each one and set the genes according to that. */ + + for (let i = 0; i < combinations.length; i++) { + + let botConfig = JSON.parse(JSON.stringify(configObj)); + let combination = combinations[i]; + let genes = {}; + let clonKey = ""; + + for (let j = 0; j < botConfig.genes.length; j++) { + + genes[botConfig.genes[j].name] = combination[j]; + clonKey = clonKey + "." + combination[j]; + + } + + let clonName = "Clon" + clonKey; + let clonConfig = JSON.parse(JSON.stringify(botConfig)); + + /* Lets adapt the bot config so as to tune it for the new clon. */ + + clonConfig.displayName = clonConfig.displayName + " " + clonName; + clonConfig.codeName = clonConfig.codeName + "-" + clonName; + + /* + for (let j = 0; j < clonConfig.products.length; j++) { + + let product = clonConfig.products[j]; + + for (let k = 0; k < product.dataSets.length; k++) { + + let dataSet = product.dataSets[k]; + + dataSet.filePath = dataSet.filePath.replace("@Clon", "-" + clonName); + } + } + */ + + devTeam.bots.push(clonConfig); + + } + } else { + + /* If the bot does not have any genes at all we do what we did before clones existed */ + + devTeam.bots[j] = configObj; + + } + + } + + if (requestsSent === responsesReceived) { + + callBackFunction(global.DEFAULT_OK_RESPONSE, ecosystem, ecosystemObject); + + } + + } + catch (err) { + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> getBots -> Cloud -> onDataArrived -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + } + catch (err) { + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> getBots -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function getPlotters() { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Entering Function."); } + + /* In the next section we are loading the plotters configurations. */ + + for (let j = 0; j < devTeam.plotters.length; j++) { + + let plotter = devTeam.plotters[j]; + + requestsSent++; + + storage.readData(devTeam.codeName + "/" + "plotters", plotter.repo, plotter.configFile, true, onDataArrived); + + function onDataArrived(err, pData) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Cloud -> onDataArrived -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Cloud -> onDataArrived -> devTeam.codeName = " + devTeam.codeName); } + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Cloud -> onDataArrived -> plotter.repo = " + plotter.repo); } + if (LOG_FILE_CONTENT === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Cloud -> onDataArrived -> pData = " + pData); } + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Cloud -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Cloud -> onDataArrived -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + responsesReceived++; + + if (pData !== "{}") { // If for any reason we could not tet this config, then we just exclude this bot to improve resiliency. + + pData = pData.toString(); + pData = pData.trim(); // remove first byte with some encoding. + + let configObj = JSON.parse(pData); + + /* Since we are going to replace the full plotter object and we dont want to lose these two properties, we do this: */ + + configObj.repo = plotter.repo; + configObj.configFile = plotter.configFile; + + devTeam.plotters[j] = configObj; + + } + + if (requestsSent === responsesReceived) { + + callBackFunction(global.DEFAULT_OK_RESPONSE, ecosystem, ecosystemObject); + + } + } + catch (err) { + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Cloud -> onDataArrived -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + } + catch (err) { + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> getPlotters -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function addStoragePermissions(pConfigObj) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ConfigReader -> readDevTeamsConfigs -> addStoragePermissions -> Entering function."); } + + if (pConfigObj.storage !== undefined) { return; } // If this information is at the config file, then we take it, otherwise, we define it here. + + let fileUri; + let sas; + + switch (serverConfig.environment) { + + case "Develop": { + + fileUri = serverConfig.productsStorage.Develop.fileUri; + sas = serverConfig.productsStorage.Develop.sas; + + break; + } + + case "Production": { + + fileUri = serverConfig.productsStorage.Production.fileUri; + sas = serverConfig.productsStorage.Production.sas; + + break; + } + } + + pConfigObj.storage = { + sas: sas, + fileUri: fileUri + }; + + } + catch (err) { + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> addStoragePermissions -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } + catch (err) { + console.log("[ERROR] ConfigReader -> readDevTeamsConfigs -> Error = " + err); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } +} \ No newline at end of file diff --git a/Server/Exchange/ExchangeAPI.js b/Server/Exchange/ExchangeAPI.js new file mode 100644 index 000000000..203259e3a --- /dev/null +++ b/Server/Exchange/ExchangeAPI.js @@ -0,0 +1,304 @@ +exports.newExchangeAPI = function newExchangeAPI(botDisplayName, authToken) { + + /* + + This module allows trading bots to connect to the exchange and do trading operations on it. So far it can only work with Poloniex. + + */ + const _ = require('lodash'); + const isValidOrder = require('./exchangeUtils').isValidOrder; + const graphqlClient = require('graphql-client') + + const MASTER_APP_API = 'https://app-api.advancedalgos.net/graphql'; + //const MASTER_APP_API = 'http://localhost:4100/graphql'; + + let MODULE_NAME = "Exchange API"; + + let thisObject = { + initialize: initialize, + getTicker: getTicker, + getOpenPositions: getOpenPositions, + getExecutedTrades: getExecutedTrades, + putPosition: putPosition, + movePosition: movePosition, + getPublicTradeHistory: getPublicTradeHistory, + getExchangeProperties: getExchangeProperties, + getMaxDecimalPositions: getMaxDecimalPositions + }; + + let apiClient; + + return thisObject; + + function initialize(callBackFunction) { + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> initialize -> Entering function."); } + + //TODO From financial beings we get the exchange the bot is trading + let botExchange = 'Poloniex'; + let exchange = botExchange.toLowerCase() + 'Client.js'; + let api = require('./Wrappers/' + exchange); + + if (!authToken) { + console.log("[ERROR] Exchange API -> initialize -> User Not Logged In."); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } else { + let keyVaultAPI = createKeyVaultAPIClient(authToken) + apiClient = api.newAPIClient(keyVaultAPI); + + callBackFunction(global.DEFAULT_OK_RESPONSE); + } + } catch (err) { + console.log("[ERROR] ExchangeAPI -> initialize -> err = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function createKeyVaultAPIClient(authToken) { + if (CONSOLE_LOG === true) { console.log("[INFO] createKeyVaultAPIClient -> Entering function."); } + + const keyVaultAPI = graphqlClient({ + url: MASTER_APP_API, + headers: { + Authorization: 'Bearer ' + authToken + } + }); + + keyVaultAPI.signTransaction = function (transaction, next) { + let variables = { + botId: botDisplayName, + transaction: transaction + } + + keyVaultAPI.query(` + mutation($botId: String, $transaction: String!){ + keyVault_SignTransaction(botId: $botId, transaction: $transaction){ + key, + signature, + date + } + }`, variables, function (req, res) { + if (res.status === 401) { + next(undefined, 'Error from graphql: Not authorized'); + } + }).then(res => { + if (res.errors) { + next(undefined, 'Error from graphql: ' + res.errors); + } else { + let signature = { + Key: res.data.keyVault_SignTransaction.key, + Sign: res.data.keyVault_SignTransaction.signature + } + next(signature) + } + }).catch(error => { + next(undefined, 'Error signing the message on the key vault: ' + error.message); + }); + } + + if (CONSOLE_LOG === true) { console.log("[INFO] createKeyVaultAPIClient -> Returning graphql client."); } + + return keyVaultAPI; + } + + /* + * Position Object = { + * id, String + * type, String + * rate, Number + * amountA, Number + * amountB, Number + * fee, Number + * datetime Date + * }; + */ + function truncDecimals(pFloatValue) { + let decimals = getMaxDecimalPositions(); + return parseFloat(parseFloat(pFloatValue).toFixed(decimals)); + } + + /* + * Return number of decimals for the current market + */ + function getMaxDecimalPositions() { + return getMarketConfig().maxDecimals; + } + + /* + * Return number of decimals for the current market + */ + function getMarketConfig() { + return _.find(getExchangeProperties().markets, (p) => { + return _.first(p.pair) === global.MARKET.assetA.toUpperCase() && + _.last(p.pair) === global.MARKET.assetB.toUpperCase(); + }); + } + + /* + * Return number of decimals for the current market + */ + function getExchangeProperties() { + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> getExchangeProperties -> Entering function."); } + + return apiClient.getExchangeProperties(); + + } catch (err) { + console.log("[ERROR] ExchangeAPI -> getExchangeProperties -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Returns the price for a given pair of assets. + * The object returned is an array of trades: + * ticker = { + * bid, Number + * ask, Number + * last Number + * }; + */ + function getTicker(pMarket, callBack) { + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> getTicker -> Entering function."); } + + apiClient.getTicker(pMarket, callBack); + + } catch (err) { + console.log("[ERROR] ExchangeAPI -> getTicker -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Returns the open positions ath the exchange for a given market and user account. + * The object returned is an array of positions + * + */ + function getOpenPositions(pMarket, callBack) { + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> getOpenPositions -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> getOpenPositions -> pMarket = " + JSON.stringify(pMarket)); } + + apiClient.getOpenPositions(pMarket, callBack); + + } catch (err) { + console.log("[ERROR] ExchangeAPI -> getOpenPositions -> Error = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Returns the trades for a given order number. + * The object returned is an array of positions + */ + function getExecutedTrades(pPositionId, callBack) { + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> getExecutedTrades -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> getExecutedTrades -> pPositionId = " + pPositionId); } + + apiClient.getExecutedTrades(pPositionId, callBack); + + } catch (err) { + console.log("[ERROR] ExchangeAPI -> getExecutedTrades -> Error = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Creates a new buy or sell order. + * The orderNumber is returned. String + */ + function putPosition(pMarket, pType, pRate, pAmountA, pAmountB, callBack) { + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> putPosition -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> putPosition -> pMarket = " + JSON.stringify(pMarket)); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> putPosition -> pType = " + pType); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> putPosition -> pRate = " + truncDecimals(pRate)); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> putPosition -> pAmountA = " + truncDecimals(pAmountA)); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> putPosition -> pAmountB = " + truncDecimals(pAmountB)); } + + let check = isValidOrder({ + market: getMarketConfig(), + api: apiClient, + amount: truncDecimals(pAmountB), + price: truncDecimals(pRate) + }); + + if (check.valid) { + if (pType === "buy") { + apiClient.buy(pMarket.assetA, pMarket.assetB, truncDecimals(pRate), truncDecimals(pAmountB), callBack); + return; + } + + if (pType === "sell") { + apiClient.sell(pMarket.assetA, pMarket.assetB, truncDecimals(pRate), truncDecimals(pAmountB), callBack); + return; + } + + console.log("[ERROR] ExchangeAPI -> putPosition -> pType must be either 'buy' or 'sell'."); + callBack(global.DEFAULT_FAIL_RESPONSE); + + } else { + console.log("[ERROR] ExchangeAPI -> putPosition -> The order is invalid: " + check.reason); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + + } catch (err) { + console.log("[ERROR] ExchangeAPI -> putPosition -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Move an existing position to the new rate. + * The new orderNumber is returned. + */ + function movePosition(pPosition, pNewRate, pNewAmountB, callBack) { + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> movePosition -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> movePosition -> pPosition = " + JSON.stringify(pPosition)); } + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> movePosition -> pNewRate = " + truncDecimals(pNewRate)); } + + apiClient.movePosition(pPosition, truncDecimals(pNewRate), truncDecimals(pNewAmountB), callBack); + + } catch (err) { + console.log("[ERROR] ExchangeAPI -> movePosition -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId. + * It's possible that the exchange doesn't support this method. + * The object returned is an array of trades: + * trade = { + * tradeID, String + * globalTradeID, String + * type, String + * rate, Number + * amountA, Number + * amountB, Number + * date Date + * }; + */ + function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) { + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] ExchangeAPI -> getTradeHistory -> Entering function."); } + + apiClient.getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack); + + } catch (err) { + console.log("[ERROR] ExchangeAPI -> getTradeHistory -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } +}; \ No newline at end of file diff --git a/Server/Exchange/Wrappers/beautifier.js b/Server/Exchange/Wrappers/beautifier.js new file mode 100644 index 000000000..5e5e6a051 --- /dev/null +++ b/Server/Exchange/Wrappers/beautifier.js @@ -0,0 +1,183 @@ +const _ = require('underscore'); + +class Beautifier { + + constructor() { + this._beautifications = { + aggTrades: { + a: 'aggTradeId', + p: 'price', + q: 'quantity', + f: 'firstTradeId', + l: 'lastTradeId', + T: 'timestamp', + m: 'maker', + M: 'bestPriceMatch' + }, + klines: { + 0: 'openTime', + 1: 'open', + 2: 'high', + 3: 'low', + 4: 'close', + 5: 'volume', + 6: 'closeTime', + 7: 'quoteAssetVolume', + 8: 'trades', + 9: 'takerBaseAssetVolume', + 10: 'takerQuoteAssetVolume', + 11: 'ignored' + }, + bids: [ + { + 0: 'price', + 1: 'quantity', + 2: 'ignored' + } + ], + asks: [ + { + 0: 'price', + 1: 'quantity', + 2: 'ignored' + } + ], + depthUpdateEvent: { + e: 'eventType', + E: 'eventTime', + s: 'symbol', + u: 'updateId', + b: 'bidDepthDelta', + a: 'askDepthDelta' + }, + bidDepthDelta: [ + { + 0: 'price', + 1: 'quantity', + 2: 'ignored' + } + ], + askDepthDelta: [ + { + 0: 'price', + 1: 'quantity', + 2: 'ignored' + } + ], + klineEvent: { + e: 'eventType', + E: 'eventTime', + s: 'symbol', + k: 'kline' + }, + kline: { + t: 'startTime', + T: 'endTime', + s: 'symbol', + i: 'interval', + f: 'firstTradeId', + L: 'lastTradeId', + o: 'open', + c: 'close', + h: 'high', + l: 'low', + v: 'volume', + n: 'trades', + x: 'final', + q: 'quoteVolume', + V: 'volumeActive', + Q: 'quoteVolumeActive', + B: 'ignored' + }, + aggTradeEvent: { + e: 'eventType', + E: 'eventTime', + s: 'symbol', + a: 'tradeId', + p: 'price', + q: 'quantity', + f: 'firstTradeId', + l: 'lastTradeId', + T: 'time', + m: 'maker', + M: 'ignored' + }, + outboundAccountInfoEvent: { + e: 'eventType', + E: 'eventTime', + m: 'makerCommission', + t: 'takerCommission', + b: 'buyerCommission', + s: 'sellerCommission', + T: 'canTrade', + W: 'canWithdraw', + D: 'canDeposit', + B: 'balances' + }, + balances: [ + { + a: 'asset', + f: 'availableBalance', + l: 'onOrderBalance' + } + ], + executionReportEvent: { + e: 'eventType', + E: 'eventTime', + s: 'symbol', + c: 'newClientOrderId', + S: 'side', + o: 'orderType', + f: 'cancelType', // GTC 'Good till Cancel', IOC: 'Immediate or Cancel' + q: 'quantity', + p: 'price', + x: 'executionType', + X: 'orderStatus', + r: 'rejectReason', + i: 'orderId', + l: 'lastTradeQuantity', + z: 'accumulatedQuantity', + L: 'lastTradePrice', + n: 'commission', + N: 'commissionAsset', + m: 'maker', + T: 'tradeTime', + t: 'tradeId' + } + } + } + + beautify(object, type) { + const beautifications = _.isObject(type) ? type : this._beautifications[type]; + if (!beautifications) { + return object; + } + let newItem = {}; + _.each(object, (value, key) => { + if (_.isArray(value)) { + const newArray = []; + if (_.isArray(this._beautifications[beautifications[key]])) { + _.each(value, arrayValue => { + newArray.push(this.beautify(arrayValue, this._beautifications[beautifications[key]][0])); + }); + } else { + _.each(value, (value, key) => { + newItem.push(this.beautify(value, beautifications[key])) + }); + } + const newKey = beautifications[key] || key; + newItem[newKey] = newArray; + } else if (_.isObject(value)) { + const newKey = beautifications[key] || key; + newItem[newKey] = this.beautify(value, beautifications[key]); + } else { + const newKey = beautifications[key] || key; + newItem[newKey] = value; + } + }); + return newItem; + } + +} + +module.exports = Beautifier; \ No newline at end of file diff --git a/Server/Exchange/Wrappers/binance.js b/Server/Exchange/Wrappers/binance.js new file mode 100644 index 000000000..624c0fc06 --- /dev/null +++ b/Server/Exchange/Wrappers/binance.js @@ -0,0 +1,368 @@ +// Source: https://github.com/zoeyg/binance/blob/master/lib/rest.js + +const request = require('request'); +const qs = require('querystring'); +const _ = require('underscore'); +const crypto = require('crypto'); +const Beautifier = require('./beautifier.js'); +const assert = require('assert'); + +class BinanceRest { + + constructor({ + key, + secret, + recvWindow = false, + timeout = 15000, + disableBeautification = false, + handleDrift = false + }) { + this.key = key; + this.secret = secret; + this.recvWindow = recvWindow; + this.timeout = timeout; + this.disableBeautification = disableBeautification; + this.handleDrift = handleDrift; + + this._beautifier = new Beautifier(); + this._baseUrl = 'https://api.binance.com/'; + this._drift = 0; + this._syncInterval = 0; + } + + _makeRequest(query, callback, route, security, method, attempt = 0) { + assert(_.isUndefined(callback) || _.isFunction(callback), 'callback must be a function or undefined'); + assert(_.isObject(query), 'query must be an object'); + + let queryString; + const type = _.last(route.split('/')), + options = { + url: `${this._baseUrl}${route}`, + timeout: this.timeout + }; + + if (security === 'SIGNED') { + if (this.recvWindow) { + query.recvWindow = this.recvWindow; + } + queryString = qs.stringify(query); + options.url += '?' + queryString; + if (options.url.substr(options.url.length - 1) !== '?') { + options.url += '&'; + } + options.url += `signature=${this._sign(queryString)}`; + } else { + queryString = qs.stringify(query); + if (queryString) { + options.url += '?' + queryString; + } + } + if (security === 'API-KEY' || security === 'SIGNED') { + options.headers = { 'X-MBX-APIKEY': this.key }; + } + if (method) { + options.method = method; + } + + const action = cb => { + request(options, (err, response, body) => { + let payload; + try { + payload = JSON.parse(body); + } catch (e) { + payload = body; + } + if (err) { + cb(err, payload); + } else if (response.statusCode < 200 || response.statusCode > 299) { + /* + * If we get a response that the timestamp is ahead of the server, + * calculate the drift and then attempt the request again + */ + if (response.statusCode === 400 && payload.code === -1021 && + this.handleDrift && attempt === 0) { + + this.calculateDrift() + .then(() => { + query.timestamp = this._getTime() + this._drift; + return this._makeRequest(query, cb, route, security, method, + ++attempt); + }); + } else { + cb(new Error(`Response code ${response.statusCode}`), payload); + } + } else { + if (_.isArray(payload)) { + payload = _.map(payload, (item) => { + return this._doBeautifications(item, type); + }); + } else { + payload = this._doBeautifications(payload); + } + cb(err, payload); + } + }); + }; + if (callback) { + action(callback); + } else { + return new Promise((resolve, reject) => action((err, payload) => { + if (err) { + if (payload === undefined) { + reject(err); + } else { + reject(payload); + } + } else { + resolve(payload); + } + })); + } + } + + _doBeautifications(response, route) { + if (this.disableBeautification) { + return response; + } + return this._beautifier.beautify(response, route); + } + + _sign(queryString) { + return crypto.createHmac('sha256', this.secret) + .update(queryString) + .digest('hex'); + } + + _setTimestamp(query) { + if (!query.timestamp) { + query.timestamp = this._getTime() + this._drift; + } + } + + _getTime() { + return new Date().getTime(); + } + + calculateDrift() { + const systemTime = this._getTime(); + return this.time() + .then((response) => { + // Calculate the approximate trip time from here to binance + const transitTime = parseInt((this._getTime() - systemTime) / 2); + this._drift = response.serverTime - (systemTime + transitTime); + }); + } + + startTimeSync(interval = 300000, onRecalculateDriftCb) { + return new Promise((resolve, reject) => { + // If there's already an interval running, clear it and reset values + if (this._syncInterval !== 0) { + this.endTimeSync(); + return resolve(); + } + + // Calculate initial drift value and setup interval to periodically update it + this.calculateDrift() + .then(resolve) + .catch(reject); + + this._syncInterval = setInterval(() => { + const promise = this.calculateDrift(); + + if (_.isFunction(onRecalculateDriftCb)) { + onRecalculateDriftCb(promise); + } + }, interval); + }); + } + + endTimeSync() { + clearInterval(this._syncInterval); + this._drift = 0; + this._syncInterval = 0; + } + + // Public APIs + ping(callback) { + return this._makeRequest({}, callback, 'api/v1/ping'); + } + + time(callback) { + return this._makeRequest({}, callback, 'api/v1/time'); + } + + depth(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + + return this._makeRequest(query, callback, 'api/v1/depth'); + } + + trades(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + + return this._makeRequest(query, callback, 'api/v1/trades'); + } + + historicalTrades(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + + return this._makeRequest(query, callback, 'api/v1/historicalTrades', 'API-KEY'); + } + + aggTrades(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + + return this._makeRequest(query, callback, 'api/v1/aggTrades'); + } + + exchangeInfo(callback) { + return this._makeRequest({}, callback, 'api/v1/exchangeInfo'); + } + + klines(query = {}, callback) { + return this._makeRequest(query, callback, 'api/v1/klines'); + } + + ticker24hr(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + + return this._makeRequest(query, callback, 'api/v1/ticker/24hr'); + } + + tickerPrice(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + + return this._makeRequest(query, callback, 'api/v3/ticker/price'); + } + + bookTicker(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + + return this._makeRequest(query, callback, 'api/v3/ticker/bookTicker'); + } + + allBookTickers(callback) { + return this._makeRequest({}, callback, 'api/v1/ticker/allBookTickers'); + } + + allPrices(callback) { + return this._makeRequest({}, callback, 'api/v1/ticker/allPrices'); + } + + // Private APIs + newOrder(query = {}, callback) { + this._setTimestamp(query); + return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED', 'POST'); + } + + testOrder(query = {}, callback) { + this._setTimestamp(query); + return this._makeRequest(query, callback, 'api/v3/order/test', 'SIGNED', 'POST'); + } + + queryOrder(query = {}, callback) { + this._setTimestamp(query); + return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED'); + } + + cancelOrder(query = {}, callback) { + this._setTimestamp(query); + return this._makeRequest(query, callback, 'api/v3/order', 'SIGNED', 'DELETE'); + } + + openOrders(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + this._setTimestamp(query); + return this._makeRequest(query, callback, 'api/v3/openOrders', 'SIGNED'); + } + + allOrders(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + this._setTimestamp(query); + return this._makeRequest(query, callback, 'api/v3/allOrders', 'SIGNED'); + } + + account(query = {}, callback) { + if (_.isFunction(query)) { + callback = query; + query = {}; + } + this._setTimestamp(query); + return this._makeRequest(query, callback, 'api/v3/account', 'SIGNED'); + } + + myTrades(query = {}, callback) { + if (_.isString(query)) { + query = { symbol: query }; + } + this._setTimestamp(query); + return this._makeRequest(query, callback, 'api/v3/myTrades', 'SIGNED'); + } + + withdraw(query = {}, callback) { + this._setTimestamp(query); + return this._makeRequest(query, callback, 'wapi/v3/withdraw.html', 'SIGNED', 'POST'); + } + + depositHistory(query = {}, callback) { + if (_.isString(query)) { + query = { asset: query }; + } + this._setTimestamp(query); + return this._makeRequest(query, callback, 'wapi/v3/depositHistory.html', 'SIGNED'); + } + + withdrawHistory(query = {}, callback) { + if (_.isString(query)) { + query = { asset: query }; + } + this._setTimestamp(query); + return this._makeRequest(query, callback, 'wapi/v3/withdrawHistory.html', 'SIGNED'); + } + + depositAddress(query = {}, callback) { + if (_.isString(query)) { + query = { asset: query }; + } + this._setTimestamp(query); + return this._makeRequest(query, callback, 'wapi/v3/depositAddress.html', 'SIGNED'); + } + + accountStatus(callback) { + const query = {}; + this._setTimestamp(query); + return this._makeRequest(query, callback, 'wapi/v3/accountStatus.html', 'SIGNED'); + } + + startUserDataStream(callback) { + return this._makeRequest({}, callback, 'api/v1/userDataStream', 'API-KEY', 'POST'); + } + + keepAliveUserDataStream(query = {}, callback) { + return this._makeRequest(query, callback, 'api/v1/userDataStream', 'API-KEY', 'PUT'); + } + + closeUserDataStream(query = {}, callback) { + return this._makeRequest(query, callback, 'api/v1/userDataStream', 'API-KEY', 'DELETE'); + } +} + +module.exports = BinanceRest; \ No newline at end of file diff --git a/Server/Exchange/Wrappers/binanceClient.js b/Server/Exchange/Wrappers/binanceClient.js new file mode 100644 index 000000000..a0c2f731e --- /dev/null +++ b/Server/Exchange/Wrappers/binanceClient.js @@ -0,0 +1,360 @@ +exports.newAPIClient = function newAPIClient(pKey, pSecret) { + + /* + /* ATENTION: This Library is used both a the cloud (AACloud) and also at the AAWeb on the server side without any modifications. + /* There is a second version, stripped of all the inner functionality that runs at AAWeb browser side, which channels the requests to the server side one. + */ + + const FULL_LOG = true; + const LOG_FILE_CONTENT = true; + const MODULE_NAME = "binanceClient"; + + const retry = require('../exchangeUtils').retry; + const includes = require('../exchangeUtils').includes; + const _ = require('lodash'); + const Binance = require("./binance"); + const exchangeProperties = require('./poloniexProperties.json'); + + const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + 'Error -1021', + 'Response code 429', + 'Response code 5', + 'ETIMEDOUT' + ]; + + let API = new Binance({ + key: pKey, + secret: pSecret, + timeout: 15000, + recvWindow: 60000, + disableBeautification: false, + handleDrift: true, + }); + + let thisObject = { + getTicker: getTicker, + getOpenPositions: getOpenPositions, + getExecutedTrades: getExecutedTrades, + buy: buy, + sell: sell, + movePosition: movePosition, + getPublicTradeHistory: getPublicTradeHistory, + getExchangeProperties: getExchangeProperties, + isValidPrice: isValidPrice, + isValidLot: isValidLot + }; + + return thisObject; + + function joinCurrencies(currencyA, currencyB) { + // If only one arg, then return the first + if (typeof currencyB !== 'string') { + return currencyA; + } + + return currencyB + '' + currencyA; + } + + /* + * Returns the properties for this exchange + */ + function getExchangeProperties() { + return exchangeProperties; + } + + function truncDecimals(pFloatValue) { + + let decimals = 6; + + return parseFloat(parseFloat(pFloatValue).toFixed(decimals)); + + } + + /* + * Returns the price for a given pair of assets. + * The object returned is an array of trades: + * ticker = { + * bid, Number + * ask, Number + * last Number + * }; + */ + function getTicker(pMarket, callBack) { + + let market = joinCurrencies(pMarket.assetA, pMarket.assetB); + const handle = (err, response) => { + let ticker; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + ticker = { + bid: Number(response.bidPrice), + ask: Number(response.askPrice), + last: Number(response.lastPrice) + }; + } + callBack(err, ticker); + }; + + const fetch = next => API.ticker24hr(market, analizeResponse(next)); + + retry(null, fetch, handle); + } + + /* + * Returns the open positions ath the exchange for a given market and user account. + * The object returned is an array of positions: + * position = { + * id, + * type, + * rate, Number + * amountA, Number + * amountB, Number + * fee, (not required) Number + * datetime + * }; + */ + function getOpenPositions(pMarket, callBack) { + + let market = joinCurrencies(pMarket.assetA, pMarket.assetB); + const handle = (err, response) => { + let exchangePositions = []; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + for (let i = 0; i < response.length; i++) { + let openPosition = { + id: response[i].orderId, + type: response[i].side.toLowerCase(), + rate: Number(response[i].price), + amountA: truncDecimals(response[i].origQty * response[i].price), + amountB: Number(response[i].origQty), + date: (new Date(response[i].time)).valueOf() + }; + exchangePositions.push(openPosition); + } + } + callBack(err, exchangePositions); + }; + + const fetch = next => API.openOrders(market, analizeResponse(next)); + + retry(null, fetch, handle); + } + + /* + * Returns the trades for a given order number. + * The object returned is an array of positions: + * position = { + * id, + * type, + * rate, Number + * amountA, Number + * amountB, Number + * fee, Number + * datetime + * }; + */ + function getExecutedTrades(pPositionId, callBack) { + + let market = joinCurrencies(global.MARKET.assetA, global.MARKET.assetB); + const handle = (err, response) => { + let exchangeTrades = []; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + for (let i = 0; i < response.length; i++) { + if (response[i].orderId === pPositionId) { + let type = "sell" + if (response[i].isBuyer === true) { + type = "buy"; + } + let trade = { + id: response[i].orderId, + type: type, + rate: Number(response[i].price), + amountA: truncDecimals(response[i].qty * response[i].price), + amountB: Number(response[i].qty), + fee: Number(response[i].commission), + date: (new Date(response[i].time)).valueOf() + }; + exchangeTrades.push(trade); + break; + } + } + } + callBack(err, exchangeTrades); + }; + + const fetch = next => API.myTrades(market, analizeResponse(next)); + + retry(null, fetch, handle); + } + + /* + * Creates a new buy order. + * The orderNumber is returned. + */ + function buy(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) { + + var parameters = { + symbol: joinCurrencies(pCurrencyA, pCurrencyB), + side: 'BUY', + type: 'LIMIT', + quantity: pAmount, + price: pRate, + timeInForce: 'GTC' + }; + const handle = (err, response) => { + let orderNumber; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + orderNumber = response.orderId; + } + callBack(err, orderNumber); + }; + + const fetch = next => API.newOrder(parameters, analizeResponse(next)); + + retry(null, fetch, handle); + + } + + /* + * Creates a new sell order. + * The orderNumber is returned. String + */ + function sell(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) { + + var parameters = { + symbol: joinCurrencies(pCurrencyA, pCurrencyB), + side: 'SELL', + type: 'LIMIT', + quantity: pAmount, + price: pRate, + timeInForce: 'GTC' + }; + const handle = (err, response) => { + let orderNumber; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + orderNumber = response.orderId; + } + callBack(err, orderNumber); + }; + + const fetch = next => API.newOrder(parameters, analizeResponse(next)); + + retry(null, fetch, handle); + } + + /* //TODO Complete partial orders + * Move an existing position to the new rate. + * The new orderNumber is returned. String + */ + function movePosition(pPosition, pNewRate, pNewAmountB, callBack) { + + var parameters = { + symbol: joinCurrencies(global.MARKET.assetA, global.MARKET.assetB), + orderId: pPosition.id + }; + + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { console.log("[INFO] moveOrder -> Step1 - Cancelling Position: " + JSON.stringify(pPosition)); } + + const handle = (err, response) => { + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + if (global.LOG_CONTROL[MODULE_NAME].logInfo === true) { console.log("[INFO] moveOrder -> Step1 Complete - Position Cancelled. Proceeding to put the new position."); } + + if (position.type === "buy") + thisObject.buy(global.MARKET.assetA, global.MARKET.assetB, pNewRate, pNewAmountB, callBack); + else + thisObject.sell(global.MARKET.assetA, global.MARKET.assetB, pNewRate, pNewAmountB, callBack); + } else { + callBack(err); + } + }; + + const fetch = next => API.cancelOrder(parameters, analizeResponse(next)); + + retry(null, fetch, handle); + } + + /* //TODO Pending Review + * Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId. + * It's possible that the exchange doesn't support this method. + * The object returned is an array of trades: + * trade = { + * tradeIdAtExchange, + * marketIdAtExchange, + * type, + * rate, Number + * amountA, Number + * amountB, Number + * datetime + * }; + */ + function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) { + + if (CONSOLE_LOG === true) { console.log("[INFO] getPublicTradeHistory -> Not implemented yet."); } + callBack(global.DEFAULT_FAIL_RESPONSE); + } + + function isValidPrice(price) { + return price >= market.minimalOrder.price; + } + + function isValidLot(price, amount) { + return amount * price >= market.minimalOrder.order; + } + + function analizeResponse(callBack) { + + return (exchangeErr, exchangeResponse) => { + let error; + + /* This function analizes the different situations we might encounter trying to access Poloniex and returns appropiate standard errors. */ + + let stringExchangeErr = JSON.stringify(exchangeErr); + let stringExchangeResponse = JSON.stringify(exchangeResponse); + + try { + if (CONSOLE_LOG === true) { console.log("[INFO] analizeResponse -> exchangeErr = " + stringExchangeErr); } + if (CONSOLE_LOG === true) { console.log("[INFO] analizeResponse -> exchangeResponse = " + stringExchangeResponse); } + + if (exchangeErr) { + error = global.DEFAULT_FAIL_RESPONSE; + } else if (!exchangeResponse) { + error = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: 'Empty response' + }; + } else if (includes(exchangeResponse.error, [''])) { + error = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: exchangeResponse + }; + } else if (includes(exchangeResponse.error, ['Order does not exist.'])) { + error = { + result: global.CUSTOM_OK_RESPONSE.result, + message: exchangeResponse.error + }; + } else if (exchangeResponse.error) { + error = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: exchangeResponse.error + }; + } + + if (error) { + if (includes(error.message, recoverableErrors)) { + error.notFatal = true; + } + } + + return callBack(error, exchangeResponse); + + } catch (err) { + console.log("[ERROR] analizeResponse -> err.message = " + err.message); + return callBack(global.DEFAULT_FAIL_RESPONSE, exchangeResponse); + } + } + } + +} diff --git a/Server/Exchange/Wrappers/binanceProperties.json b/Server/Exchange/Wrappers/binanceProperties.json new file mode 100644 index 000000000..ad4438de0 --- /dev/null +++ b/Server/Exchange/Wrappers/binanceProperties.json @@ -0,0 +1,4594 @@ +{ + "assets": [ + "NULS", + "VEN", + "BNB", + "NEO", + "LINK", + "SALT", + "IOTA", + "ETC", + "AST", + "KNC", + "WTC", + "SNGLS", + "EOS", + "SNT", + "MCO", + "BTC", + "OAX", + "OMG", + "GAS", + "BQX", + "QTUM", + "BNT", + "DNT", + "ICN", + "SNM", + "STRAT", + "ZRX", + "FUN", + "LTC", + "ETH", + "XVG", + "BCC", + "ADA", + "ADX", + "AE", + "AGI", + "AION", + "AMB", + "APPC", + "ARK", + "ARN", + "BAT", + "BCD", + "BCN", + "BCPT", + "BLZ", + "BRD", + "BTG", + "BTS", + "CDT", + "CHAT", + "CLOAK", + "CMT", + "CND", + "CVC", + "DASH", + "DATA", + "DENT", + "DGD", + "DLT", + "EDO", + "ELF", + "ENG", + "ENJ", + "EVX", + "FUEL", + "GNT", + "GRS", + "GTO", + "GVT", + "GXS", + "HSR", + "ICX", + "INS", + "IOST", + "IOTX", + "KEY", + "KMD", + "LEND", + "LOOM", + "LRC", + "LSK", + "LUN", + "MANA", + "MDA", + "MFT", + "MOD", + "MTH", + "MTL", + "NANO", + "NAS", + "NAV", + "NCASH", + "NEBL", + "NPXS", + "NXS", + "ONT", + "OST", + "PIVX", + "POA", + "POE", + "POWR", + "PPT", + "QKC", + "QLC", + "QSP", + "RCN", + "RDN", + "REP", + "REQ", + "RLC", + "RPX", + "SC", + "SKY", + "STEEM", + "STORJ", + "STORM", + "SUB", + "SYS", + "THETA", + "TNB", + "TNT", + "TRIG", + "TRX", + "TUSD", + "VIA", + "VIB", + "VIBE", + "WABI", + "WAN", + "WAVES", + "WINGS", + "WPR", + "XEM", + "XLM", + "XMR", + "XRP", + "XZC", + "YOYO", + "ZEC", + "ZEN", + "ZIL" + ], + "currencies": [ + "BNB", + "BTC", + "ETH", + "USDT" + ], + "markets": [ + { + "pair": [ + "BNB", + "NULS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BNB", + "VEN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BNB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "NULS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "NEO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NULS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "LINK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "SALT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "IOTA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "ETC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AST" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "KNC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "WTC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "SNGLS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "EOS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "SNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "MCO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "BTC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.000001, + "price": 0.01, + "order": 10 + } + }, + { + "pair": [ + "ETH", + "OAX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "OMG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "GAS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BQX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "WTC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "QTUM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "BNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "DNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "ICN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "SNM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SNM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "SNGLS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "BQX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NEO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "KNC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "STRAT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "ZRX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "QTUM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "FUN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "LTC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LINK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ETH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XVG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "STRAT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "ZRX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "IOTA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "BCC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "OMG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "MCO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SALT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "ADA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ADA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ADA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "ADA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "ADX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ADX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ADX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "AE" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "AE" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AE" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "AGI" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "AGI" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AGI" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "AION" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "AION" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AION" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "AMB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "AMB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "AMB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "APPC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "APPC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "APPC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ARK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ARK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ARN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ARN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "AST" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "BAT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BAT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BAT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "BCC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 1 + } + }, + { + "pair": [ + "ETH", + "BCC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "BCC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "BCD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BCD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "BCN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BCN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BCN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "BCPT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BCPT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BCPT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "BLZ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BLZ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BLZ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "BNB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "BNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "BRD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BRD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BRD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "BTG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BTG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "BTS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "BTS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "BTS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "CDT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CDT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "CHAT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CHAT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "CLOAK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CLOAK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "CMT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "CMT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CMT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "CND" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "CND" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CND" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "CVC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "CVC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "CVC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DASH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DASH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DATA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DATA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DENT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DENT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DGD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DGD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "DLT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "DLT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "DLT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "DNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "EDO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "EDO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ELF" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ELF" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ENG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ENG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "ENJ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ENJ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ENJ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "EOS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "EOS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "USDT", + "EOS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "ETC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "ETH", + "ETC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "ETC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "EVX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "EVX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "FUEL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "FUEL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "FUN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "GNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "GNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "GRS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GRS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "GTO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "GTO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GTO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "GVT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GVT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "GXS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "GXS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "HSR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "HSR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ICN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "ICX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ICX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ICX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "ICX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "INS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "INS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "IOST" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "IOST" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "IOTA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "IOTA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "IOTX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "IOTX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "KEY" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "KEY" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "KMD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "KMD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "LEND" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LEND" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "LOOM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "LOOM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LOOM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "LRC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LRC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "LSK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "LSK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LSK" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "LTC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 1 + } + }, + { + "pair": [ + "ETH", + "LTC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "LTC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "LUN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "LUN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "MANA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MANA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "MCO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "MDA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MDA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "MFT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "MFT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MFT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "MOD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MOD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "MTH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MTH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "MTL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "MTL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NANO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NANO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NANO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NAS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NAS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NAS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NAV" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NAV" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NAV" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NCASH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NCASH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NCASH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NEBL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NEBL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NEBL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NEO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "NEO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "NPXS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NPXS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "NXS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "NXS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "NXS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "OAX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "ONT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ONT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ONT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "ONT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "OST" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "OST" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "OST" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "PIVX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "PIVX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "PIVX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "POA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "POA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "POA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "POE" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "POE" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "POWR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "POWR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "POWR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "PPT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "PPT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "QKC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "QKC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "QLC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "QLC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "QLC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "QSP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "QSP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "QSP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "QTUM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "USDT", + "QTUM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "RCN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "RCN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "RCN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "RDN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "RDN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "RDN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "REP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "REP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "REP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "REQ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "REQ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "RLC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "RLC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "RLC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "RPX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "RPX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "RPX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "SC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "SC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "SKY" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "SKY" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SKY" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "SNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "STEEM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "STEEM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "STEEM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "STORJ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "STORJ" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "STORM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "STORM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "STORM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "SUB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SUB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "SYS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "SYS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "SYS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "THETA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "THETA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "THETA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "TNB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TNB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "TNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TNT" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "TRIG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "TRIG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TRIG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "TRX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TRX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "TRX" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "TUSD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "TUSD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "TUSD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "TUSD" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "VEN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "VEN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "VEN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "BNB", + "VIA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "VIA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "VIA" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "VIB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BTC", + "VIBE" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "VIBE" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "ETH", + "VIB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "WABI" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "WABI" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WABI" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "WAN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "WAN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WAN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "WAVES" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "WAVES" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 1e-7, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WAVES" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "WINGS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WINGS" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-7, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "WPR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "WPR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "WTC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 1 + } + }, + { + "pair": [ + "BNB", + "XEM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "XEM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XEM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "XLM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "XLM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XLM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "XLM" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "XMR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XMR" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "XRP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.00001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "XRP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XRP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "XRP" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.1, + "price": 0.00001, + "order": 10 + } + }, + { + "pair": [ + "BTC", + "XVG" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "BNB", + "XZC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "XZC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "XZC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.000001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "YOYO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "YOYO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "YOYO" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "BTC", + "ZEC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ZEC" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "ZEN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ZEN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.000001, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ZEN" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.001, + "price": 0.00001, + "order": 0.01 + } + }, + { + "pair": [ + "BNB", + "ZIL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 0.000001, + "order": 1 + } + }, + { + "pair": [ + "BTC", + "ZIL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.001 + } + }, + { + "pair": [ + "ETH", + "ZIL" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 1, + "price": 1e-8, + "order": 0.01 + } + }, + { + "pair": [ + "USDT", + "BNB" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.01, + "price": 0.0001, + "order": 10 + } + }, + { + "pair": [ + "USDT", + "ETH" + ], + "maxDecimals": 6, + "minimalOrder": { + "amount": 0.00001, + "price": 0.01, + "order": 10 + } + } + ] +} \ No newline at end of file diff --git a/Server/Exchange/Wrappers/poloniex.js b/Server/Exchange/Wrappers/poloniex.js new file mode 100644 index 000000000..dc48c5802 --- /dev/null +++ b/Server/Exchange/Wrappers/poloniex.js @@ -0,0 +1,440 @@ +// Source: https://github.com/premasagar/poloniex.js +// Documentation: https://poloniex.com/support/api/ + +module.exports = (function () { + 'use strict'; + + // Module dependencies + var request = require('request'), + nonce = require('nonce')(); + + // Constants + var version = '0.0.9', + PUBLIC_API_URL = 'https://poloniex.com/public', + PRIVATE_API_URL = 'https://poloniex.com/tradingApi', + USER_AGENT = 'poloniex.js ' + version; + //USER_AGENT = 'Mozilla/5.0 (Windows NT 6.3; WOW64; rv:26.0) Gecko/20100101 Firefox/26.0' + + // Helper methods + function joinCurrencies(currencyA, currencyB) { + // If only one arg, then return the first + if (typeof currencyB !== 'string') { + return currencyA; + } + + return currencyA + '_' + currencyB; + } + + // Constructor + function Poloniex(keyVaultAPI) { + // Generate headers signed by this user's key and secret. + // The secret is encapsulated and never exposed + this._getPrivateHeaders = function (parameters, next) { + var paramString, signature; + + // Convert to `arg1=foo&arg2=bar` + paramString = Object.keys(parameters).map(function (param) { + return encodeURIComponent(param) + '=' + encodeURIComponent(parameters[param]); + }).join('&'); + + keyVaultAPI.signTransaction(paramString, next); + }; + } + + // Currently, this fails with `Error: CERT_UNTRUSTED` + // Poloniex.STRICT_SSL can be set to `false` to avoid this. Use with caution. + // Will be removed in future, once this is resolved. + Poloniex.STRICT_SSL = true; + + // Customisable user agent string + Poloniex.USER_AGENT = USER_AGENT; + + // Prototype + Poloniex.prototype = { + constructor: Poloniex, + + // Make an API request + _request: function (options, callback) { + if (!('headers' in options)) { + options.headers = {}; + } + + options.json = true; + options.headers['User-Agent'] = Poloniex.USER_AGENT; + options.strictSSL = Poloniex.STRICT_SSL; + options.timeout = 60 * 1000; + + request(options, function (err, response, body) { + // Empty response + if (!err && (typeof body === 'undefined' || body === null)) { + err = 'Empty response'; + } + + callback(err, body); + }); + + return this; + }, + + // Make a public API request + _public: function (command, parameters, callback) { + var options; + + if (typeof parameters === 'function') { + callback = parameters; + parameters = {}; + } + + parameters || (parameters = {}); + parameters.command = command; + options = { + method: 'GET', + url: PUBLIC_API_URL, + qs: parameters + }; + + options.qs.command = command; + return this._request(options, callback); + }, + + // Make a private API request + _private: function (command, parameters, callback) { + var options; + + if (typeof parameters === 'function') { + callback = parameters; + parameters = {}; + } + + parameters || (parameters = {}); + parameters.command = command; + parameters.nonce = nonce(); + + options = { + method: 'POST', + url: PRIVATE_API_URL, + form: parameters + }; + + let headers = this._getPrivateHeaders(parameters, next) + + let thisObject = this + + function next(pHeaders, error) { + if (!error) { + options.headers = pHeaders; + return thisObject._request(options, callback); + } else { + callback(error); + } + + } + }, + + ///// + // PUBLIC METHODS + + returnTicker: function (callback) { + return this._public('returnTicker', callback); + }, + + return24hVolume: function (callback) { + return this._public('return24hVolume', callback); + }, + + returnOrderBook: function (currencyA, currencyB, callback) { + var currencyPair; + + if (typeof currencyB === 'function') { + currencyPair = currencyA; + callback = currencyB; + currencyB = null; + } + + else { + currencyPair = joinCurrencies(currencyA, currencyB); + } + + var parameters = { + currencyPair: currencyPair + }; + + return this._public('returnOrderBook', parameters, callback); + }, + + returnChartData: function (currencyA, currencyB, period, start, end, callback) { + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB), + period: period, + start: start, + end: end + }; + + return this._public('returnChartData', parameters, callback); + }, + + returnCurrencies: function (callback) { + return this._public('returnCurrencies', callback); + }, + + returnLoanOrders: function (currency, callback) { + return this._public('returnLoanOrders', { currency: currency }, callback); + }, + + returnTradeHistory: function (currencyA, currencyB, start, end, callback) { + if (arguments.length < 5) { + callback = start; + start = Date.now() / 1000 - 60 * 60; + end = Date.now() / 1000 + 60 * 60; // Some buffer in case of client/server time out of sync. + } + + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB), + start: start, + end: end + }; + + return this._public('returnTradeHistory', parameters, callback); + }, + + ///// + // PRIVATE METHODS + + returnBalances: function (callback) { + return this._private('returnBalances', {}, callback); + }, + + returnCompleteBalances: function (account, callback) { + var parameters = {}; + + if (typeof account === 'function') { + callback = account; + } + + else if (typeof account === 'string' && !!account) { + parameters.account = account; + } + + return this._private('returnCompleteBalances', parameters, callback); + }, + + returnDepositAddresses: function (callback) { + return this._private('returnDepositAddresses', {}, callback); + }, + + generateNewAddress: function (currency, callback) { + return this._private('generateNewAddress', { currency: currency }, callback); + }, + + returnDepositsWithdrawals: function (start, end, callback) { + return this._private('returnDepositsWithdrawals', { start: start, end: end }, callback); + }, + + // can be called with `returnOpenOrders('all', callback)` + returnOpenOrders: function (currencyA, currencyB, callback) { + var currencyPair; + + if (typeof currencyB === 'function') { + currencyPair = currencyA; + callback = currencyB; + currencyB = null; + } + + else { + currencyPair = joinCurrencies(currencyA, currencyB); + } + + var parameters = { + currencyPair: currencyPair + }; + + return this._private('returnOpenOrders', parameters, callback); + }, + + returnOrderTrades: function (orderNumber, callback) { + var parameters = { + orderNumber: orderNumber + }; + + return this._private('returnOrderTrades', parameters, callback); + }, + + buy: function (currencyA, currencyB, rate, amount, callback) { + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB), + rate: rate, + amount: amount + }; + + return this._private('buy', parameters, callback); + }, + + sell: function (currencyA, currencyB, rate, amount, callback) { + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB), + rate: rate, + amount: amount + }; + + return this._private('sell', parameters, callback); + }, + + cancelOrder: function (currencyA, currencyB, orderNumber, callback) { + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB), + orderNumber: orderNumber + }; + + return this._private('cancelOrder', parameters, callback); + }, + + moveOrder: function (orderNumber, rate, amount, callback) { + var parameters = { + orderNumber: orderNumber, + rate: rate, + amount: amount ? amount : null + }; + + return this._private('moveOrder', parameters, callback); + }, + + withdraw: function (currency, amount, address, callback) { + var parameters = { + currency: currency, + amount: amount, + address: address + }; + + return this._private('withdraw', parameters, callback); + }, + + returnFeeInfo: function (callback) { + return this._private('returnFeeInfo', {}, callback); + }, + + returnAvailableAccountBalances: function (account, callback) { + var parameters = {}; + + if (typeof account === 'function') { + callback = account; + } + + else if (typeof account === 'string' && !!account) { + parameters.account = account; + } + + return this._private('returnAvailableAccountBalances', parameters, callback); + }, + + returnTradableBalances: function (callback) { + return this._private('returnTradableBalances', {}, callback); + }, + + transferBalance: function (currency, amount, fromAccount, toAccount, callback) { + var parameters = { + currency: currency, + amount: amount, + fromAccount: fromAccount, + toAccount: toAccount + }; + + return this._private('transferBalance', parameters, callback); + }, + + returnMarginAccountSummary: function (callback) { + return this._private('returnMarginAccountSummary', {}, callback); + }, + + marginBuy: function (currencyA, currencyB, rate, amount, lendingRate, callback) { + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB), + rate: rate, + amount: amount, + lendingRate: lendingRate ? lendingRate : null + }; + + return this._private('marginBuy', parameters, callback); + }, + + marginSell: function (currencyA, currencyB, rate, amount, lendingRate, callback) { + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB), + rate: rate, + amount: amount, + lendingRate: lendingRate ? lendingRate : null + }; + + return this._private('marginSell', parameters, callback); + }, + + getMarginPosition: function (currencyA, currencyB, callback) { + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB) + }; + + return this._private('getMarginPosition', parameters, callback); + }, + + closeMarginPosition: function (currencyA, currencyB, callback) { + var parameters = { + currencyPair: joinCurrencies(currencyA, currencyB) + }; + + return this._private('closeMarginPosition', parameters, callback); + }, + + createLoanOffer: function (currency, amount, duration, autoRenew, lendingRate, callback) { + var parameters = { + currency: currency, + amount: amount, + duration: duration, + autoRenew: autoRenew, + lendingRate: lendingRate + }; + + return this._private('createLoanOffer', parameters, callback); + }, + + cancelLoanOffer: function (orderNumber, callback) { + var parameters = { + orderNumber: orderNumber + }; + + return this._private('cancelLoanOffer', parameters, callback); + }, + + returnOpenLoanOffers: function (callback) { + return this._private('returnOpenLoanOffers', {}, callback); + }, + + returnActiveLoans: function (callback) { + return this._private('returnActiveLoans', {}, callback); + }, + + returnLendingHistory: function (start, end, limit, callback) { + var parameters = { + start: start, + end: end, + limit: limit + }; + + return this._private('returnLendingHistory', parameters, callback); + }, + + toggleAutoRenew: function (orderNumber, callback) { + return this._private('toggleAutoRenew', { orderNumber: orderNumber }, callback); + } + + }; + + // Backwards Compatibility + Poloniex.prototype.getTicker = Poloniex.prototype.returnTicker; + Poloniex.prototype.get24hVolume = Poloniex.prototype.return24hVolume; + Poloniex.prototype.getOrderBook = Poloniex.prototype.returnOrderBook; + Poloniex.prototype.getTradeHistory = Poloniex.prototype.returnChartData; + Poloniex.prototype.myBalances = Poloniex.prototype.returnBalances; + Poloniex.prototype.myOpenOrders = Poloniex.prototype.returnOpenOrders; + Poloniex.prototype.myTradeHistory = Poloniex.prototype.returnTradeHistory; + + return Poloniex; +})(); diff --git a/Server/Exchange/Wrappers/poloniexClient.js b/Server/Exchange/Wrappers/poloniexClient.js new file mode 100644 index 000000000..eb26a7f66 --- /dev/null +++ b/Server/Exchange/Wrappers/poloniexClient.js @@ -0,0 +1,406 @@ +exports.newAPIClient = function newAPIClient(keyVaultAPI) { + + /* + /* ATENTION: This Library is used both a the cloud (AACloud) and also at the AAWeb on the server side without any modifications. + /* There is a second version, stripped of all the inner functionality that runs at AAWeb browser side, which channels the requests to the server side one. + */ + + const FULL_LOG = true; + const LOG_FILE_CONTENT = true; + const MODULE_NAME = "poloniexClient"; + + const retry = require('../exchangeUtils').retry; + const includes = require('../exchangeUtils').includes; + const _ = require('lodash'); + const Poloniex = require("./poloniex"); + const exchangeProperties = require('./poloniexProperties.json'); + + const recoverableErrors = [ + 'SOCKETTIMEDOUT', + 'TIMEDOUT', + 'CONNRESET', + 'CONNREFUSED', + 'NOTFOUND', + '429', + '522', + '504', + '503', + '500', + '502', + 'Empty response', + 'Please try again in a few minutes.', + 'Nonce must be greater than', + 'Internal error. Please try again.', + 'Connection timed out. Please try again.', + 'ENOTFOUND', + 'ECONNREFUSED', + 'CONNREFUSED', + 'NOTFOUND', + 'ESOCKETTIMEDOUT', + 'ECONNRESET', + 'Connection timed out', + 'Connection Error', + 'Order not found, or you are not the person who placed it.', + 'ETIMEDOUT' + ]; + + let API = new Poloniex(keyVaultAPI); + + let thisObject = { + getTicker: getTicker, + getOpenPositions: getOpenPositions, + getExecutedTrades: getExecutedTrades, + buy: buy, + sell: sell, + movePosition: movePosition, + getPublicTradeHistory: getPublicTradeHistory, + getExchangeProperties: getExchangeProperties, + isValidLot: isValidLot + }; + + return thisObject; + + function joinCurrencies(currencyA, currencyB) { + // If only one arg, then return the first + if (typeof currencyB !== 'string') { + return currencyA; + } + + return currencyA + '_' + currencyB; + } + + + /* + * Returns the properties for this exchange + */ + function getExchangeProperties() { + return exchangeProperties; + } + + /* + * Returns the price for a given pair of assets. + * The object returned is an array of trades: + * ticker = { + * bid, Number + * ask, Number + * last Number + * }; + */ + function getTicker(pMarket, callBack) { + + const handle = (err, response) => { + let ticker; + let market = joinCurrencies(pMarket.assetA, pMarket.assetB); + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + ticker = { + bid: Number(response[market].highestBid), + ask: Number(response[market].lowestAsk), + last: Number(response[market].last) + }; + } + callBack(err, ticker); + }; + + const fetch = next => API.getTicker(analizeResponse(next)); + + retry(null, fetch, handle); + } + + /* + * Returns the open positions ath the exchange for a given market and user account. + * The object returned is an array of positions: + * position = { + * id, + * type, + * rate, Number + * amountA, Number + * amountB, Number + * fee, (not required) Number + * datetime + * }; + */ + function getOpenPositions(pMarket, callBack) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] poloniexClient -> getOpenPositions -> Entering function."); } + + const handle = (err, response) => { + let exchangePositions = []; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + for (let i = 0; i < response.length; i++) { + let openPosition = { + id: response[i].orderNumber, + type: response[i].type, + rate: Number(response[i].rate), + amountA: Number(response[i].total), + amountB: Number(response[i].amount), + date: (new Date(response[i].time)).valueOf() + }; + exchangePositions.push(openPosition); + } + } + callBack(err, exchangePositions); + }; + + const fetch = next => API.returnOpenOrders(pMarket.assetA, pMarket.assetB, analizeResponse(next)); + + retry(null, fetch, handle); + + } catch (err) { + console.log("[ERROR] poloniexClient -> getOpenPositions -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Returns the trades for a given order number. + * The object returned is an array of positions: + * position = { + * id, + * type, + * rate, Number + * amountA, Number + * amountB, Number + * fee, Number + * datetime + * }; + */ + function getExecutedTrades(pPositionId, callBack) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] poloniexClient -> getExecutedTrades -> Entering function."); } + + const handle = (err, response) => { + let trades = []; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + for (let i = 0; i < response.length; i++) { + let trade = { + id: response[i].tradeID, + type: response[i].type, + rate: Number(response[i].rate), + amountA: Number(response[i].total), + amountB: Number(response[i].amount), + fee: Number(response[i].fee), + date: (new Date(response[i].date)).valueOf() + } + trades.push(trade); + } + } else if (err.result === global.CUSTOM_OK_RESPONSE.result) { + // No trades found at the exchange for the position, but returning empy trades object. + err = global.DEFAULT_OK_RESPONSE; + } + callBack(err, trades); + }; + + const fetch = next => API.returnOrderTrades(pPositionId, analizeResponse(next)); + + retry(null, fetch, handle); + + } catch (err) { + console.log("[ERROR] poloniexClient -> getExecutedTrades -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Creates a new buy order. + * The orderNumber is returned. String + */ + function buy(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] poloniexClient -> buy -> Entering function."); } + + const handle = (err, response) => { + let orderNumber; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + orderNumber = response.orderNumber; + } + callBack(err, orderNumber); + }; + + const fetch = next => API.buy(pCurrencyA, pCurrencyB, pRate, pAmount, analizeResponse(next)); + + retry(null, fetch, handle); + + } catch (err) { + console.log("[ERROR] poloniexClient -> buy -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Creates a new sell order. + * The orderNumber is returned. String + */ + function sell(pCurrencyA, pCurrencyB, pRate, pAmount, callBack) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] poloniexClient -> sell -> Entering function."); } + + const handle = (err, response) => { + let orderNumber; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + orderNumber = response.orderNumber; + } + callBack(err, orderNumber); + }; + + const fetch = next => API.sell(pCurrencyA, pCurrencyB, pRate, pAmount, analizeResponse(next)); + + retry(null, fetch, handle); + + } catch (err) { + console.log("[ERROR] poloniexClient -> sell -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Move an existing position to the new rate. + * The orderNumber is returned. String + */ + function movePosition(pPosition, pNewRate, pNewAmountB, callBack) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] poloniexClient -> movePosition -> Entering function."); } + + const handle = (err, response) => { + let orderNumber; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + orderNumber = response.orderNumber; + } + callBack(err, orderNumber); + }; + + const fetch = next => API.moveOrder(pPosition.id, pNewRate, pNewAmountB, analizeResponse(next)); + + retry(null, fetch, handle); + + } catch (err) { + console.log("[ERROR] poloniexClient -> movePosition -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + /* + * Returns all the trade history from the Exchange since startTime to endTime orderd by tradeId. + * It's possible that the exchange doesn't support this method. + * The object returned is an array of trades: + * trade = { + * tradeID, String + * globalTradeID, String + * type, String + * rate, Number + * amountA, Number + * amountB, Number + * date Date + * }; + */ + function getPublicTradeHistory(assetA, assetB, startTime, endTime, callBack) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] poloniexClient -> getPublicTradeHistory -> Entering function."); } + + const handle = (err, response) => { + let trades = []; + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + for (let i = 0; i < response.length; i++) { + let trade = { + tradeID: response[i].tradeID, + globalTradeID: response[i].globalTradeID, + type: response[i].type, + rate: Number(response[i].rate), + total: Number(response[i].total), + amount: Number(response[i].amount), + date: new Date(response[i].date) + } + trades.push(trade); + } + } + callBack(err, trades); + }; + + const fetch = next => API.returnTradeHistory(assetA, assetB, startTime, endTime, analizeResponse(next)); + + retry(null, fetch, handle); + + } catch (err) { + console.log("[ERROR] poloniexClient -> getPublicTradeHistory -> err = " + err.message); + callBack(global.DEFAULT_FAIL_RESPONSE); + } + } + + function isValidLot(price, amount) { + return amount * price >= 0.0001; + } + + function analizeResponse(callBack) { + + return (exchangeErr, exchangeResponse) => { + let error; + + /* This function analizes the different situations we might encounter trying to access Poloniex and returns appropiate standard errors. */ + + let stringExchangeErr = JSON.stringify(exchangeErr); + let stringExchangeResponse = JSON.stringify(exchangeResponse); + + try { + if (FULL_LOG === true) { console.log("[INFO] poloniexClient -> analizeResponse -> exchangeErr = " + stringExchangeErr); } + if (FULL_LOG === true) { console.log("[INFO] poloniexClient -> analizeResponse -> exchangeResponse = " + stringExchangeResponse); } + + if (exchangeErr) { + error = global.DEFAULT_FAIL_RESPONSE; + + if (includes(exchangeErr.code, recoverableErrors)) { + error.notFatal = true; + } + } else if (!exchangeResponse || exchangeResponse === undefined) { + error = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: 'Empty response', + notFatal: true + }; + } else if (includes(exchangeResponse.error, ['Please complete the security check to proceed.'])) { + error = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: 'The IP has been flagged by CloudFlare.' + }; + } else if (includes(exchangeResponse.error, [''])) { + error = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: exchangeResponse + }; + } else if (includes(exchangeResponse.error, ['Not enough'])) { + error = { + result: global.DEFAULT_RETRY_RESPONSE.result, + message: exchangeResponse.error + }; + } else if (includes(exchangeResponse.error, ['Order not found, or you are not the person who placed it.'])) { + error = { + result: global.CUSTOM_OK_RESPONSE.result, + message: exchangeResponse.error + }; + } else if (exchangeResponse.error) { + error = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: exchangeResponse.error + }; + } + + return callBack(error, exchangeResponse); + + } catch (err) { + console.log("[ERROR] poloniexClient -> analizeResponse -> err.message = " + err.message); + return callBack(global.DEFAULT_FAIL_RESPONSE, exchangeResponse); + } + } + } + +} diff --git a/Server/Exchange/Wrappers/poloniexProperties.json b/Server/Exchange/Wrappers/poloniexProperties.json new file mode 100644 index 000000000..a5716ba3b --- /dev/null +++ b/Server/Exchange/Wrappers/poloniexProperties.json @@ -0,0 +1,3513 @@ +{ + "currencies": ["BTC", "ETH", "XMR", "USDT"], + "assets": [ + "1CR", "ABY", "AC", "ACH", "ADN", "AEON", "AERO", "AIR", "AMP", "APH", + "ARCH", "AUR", "AXIS", "BALLS", "BANK", "BBL", "BBR", "BCC", "BCH", "BCN", + "BCY", "BDC", "BDG", "BELA", "BITCNY", "BITS", "BITUSD", "BLK", "BLOCK", + "BLU", "BNS", "BONES", "BOST", "BTC", "BTCD", "BTCS", "BTM", "BTS", + "BURN", "BURST", "C2", "CACH", "CAI", "CC", "CCN", "CGA", "CHA", "CINNI", + "CLAM", "CNL", "CNMT", "CNOTE", "COMM", "CON", "CORG", "CRYPT", "CURE", + "CVC", "CYC", "DAO", "DASH", "DCR", "DGB", "DICE", "DIEM", "DIME", "DIS", "DNS", + "DOGE", "DRKC", "DRM", "DSH", "DVK", "EAC", "EBT", "ECC", "EFL", "EMC2", + "EMO", "ENC", "ETC", "ETH", "eTOK", "EXE", "EXP", "FAC", "FCN", "FCT", + "FIBRE", "FLAP", "FLDC", "FLO", "FLT", "FOX", "FRAC", "FRK", "FRQ", "GNO", + "FVZ", "FZ", "FZN", "GAME", "GAP", "GAS", "GDN", "GEMZ", "GEO", "GIAR", "GLB", + "GML", "GNS", "GNT", "GOLD", "GPC", "GPUC", "GRC", "GRCX", "GRS", "GUE", "H2O", + "HIRO", "HOT", "HUC", "HUGE", "HVC", "HYP", "HZ", "IFC", "INDEX", "IOC", + "ITC", "IXC", "JLH", "JPC", "JUG", "KDC", "KEY", "LBC", "LC", "LCL", + "LEAF", "LGC", "LOL", "LOVE", "LQD", "LSK", "LTBC", "LTC", "LTCX", + "MAID", "MAST", "MAX", "MCN", "MEC", "METH", "MIL", "MIN", "MINT", "MMC", + "MMNXT", "MMXIV", "MNTA", "MON", "MRC", "MRS", "MTS", "MUN", "MYR", + "MZC", "N5X", "NAS", "NAUT", "NAV", "NBT", "NEOS", "NL", "NMC", "NOBL", + "NOTE", "NOXT", "NRS", "NSR", "NTX", "NXT", "NXTI", "OMG", "OMNI", "OPAL", + "PAND", "PASC", "PAWN", "PIGGY", "PINK", "PLX", "PMC", "POT", "PPC", "PRC", + "PRT", "PTS", "Q2C", "QBK", "QCN", "QORA", "QTL", "RADS", "RBY", "RDD", "REP", + "RIC", "RZR", "SBD", "SC", "SDC", "SHIBE", "SHOPX", "SILK", "SJCX", + "SLR", "SMC", "SOC", "SPA", "SQL", "SRCC", "SRG", "SSD", "STEEM", "STR", + "SUM", "SUN", "SWARM", "SXC", "SYNC", "SYS", "TAC", "TOR", "TRUST", + "TWE", "UIS", "ULTC", "UNITY", "URO", "USDE", "USDT", "UTC", "UTIL", + "UVC", "VIA", "VOOT", "VOX", "VRC", "VTC", "WC", "WDC", "WIKI", "WOLF", + "X13", "XAI", "XAP", "XBC", "XC", "XCH", "XCN", "XCP", "XCR", "XDN", + "XDP", "XEM", "XHC", "XLB", "XMG", "XMR", "XPB", "XPM", "XRP", "XSI", + "XST", "XSV", "XUSD", "XVC", "XXC", "BCH", "YACC", "YANG", "YC", "YIN", "ZEC", + "ZRX" + ], + "markets": [ + { + "pair": [ + "BTC", + "1CR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ABY" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ACH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ADN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AEON" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AERO" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AIR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AMP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "APH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ARCH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AUR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "AXIS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BALLS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BANK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BBL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BBR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BCY" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BDC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BDG" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BELA" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BITCNY" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BITS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BITUSD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BLK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BLOCK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BLU" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BNS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BONES" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BOST" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTCD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTCS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BTS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BURN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "BURST" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "C2" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CACH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CAI" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CCN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CGA" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CHA" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CINNI" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CLAM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CNL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CNMT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CNOTE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "COMM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CON" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CORG" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CRYPT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CURE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CVC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "CYC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DAO" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DASH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DCR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DGB" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DICE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DIEM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DIME" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DIS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DNS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DOGE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DRKC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DRM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DSH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "DVK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EAC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EBT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ECC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EFL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EMC2" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EMO" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ENC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ETH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "eTOK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EXE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "EXP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FAC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FCN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FCT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FIBRE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FLAP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FLDC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FLO" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FLT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FOX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FRAC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FRK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FRQ" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FVZ" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FZ" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "FZN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GAME" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GAP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GAS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GDN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GEMZ" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GEO" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GIAR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GLB" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GML" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GNS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GOLD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GPC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GPUC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GRC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GRCX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GRS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "GUE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "H2O" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HIRO" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HOT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HUC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HUGE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HVC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HYP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "HZ" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IFC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "INDEX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IOC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ITC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "IXC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "JLH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "JPC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "JUG" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "KDC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "KEY" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LBC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LCL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LEAF" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LGC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LOL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LOVE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LQD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LSK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LTBC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LTC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "LTCX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MAID" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MAST" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MAX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MCN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MEC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "METH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MIL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MIN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MINT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MMC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MMNXT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MMXIV" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MNTA" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MON" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MRC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MRS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MTS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MUN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MYR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "MZC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "N5X" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NAS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NAUT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NAV" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NBT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NEOS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NMC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NOBL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NOTE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NOXT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NRS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NSR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NTX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NXT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "NXTI" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "OMG" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "OMNI" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "OPAL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PAND" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PASC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PAWN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PIGGY" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PINK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PLX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PMC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "POT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PPC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PRC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PRT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "PTS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "Q2C" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QBK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QCN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QORA" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "QTL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RADS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RBY" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RDD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "REP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RIC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "RZR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SBD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SDC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SHIBE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SHOPX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SILK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SJCX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SLR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SMC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SOC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SPA" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SQL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SRCC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SRG" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SSD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "STEEM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "STR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SUM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SUN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SWARM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SXC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SYNC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "SYS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TAC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TOR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TRUST" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "TWE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UIS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ULTC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UNITY" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "URO" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "USDE" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "USDT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UTC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UTIL" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "UVC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VIA" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VOOT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VOX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VRC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "VTC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WDC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WIKI" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "WOLF" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "X13" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XAI" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XAP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XBC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XCH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XCN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XCP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XCR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XDN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XDP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XEM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XHC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XLB" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XMG" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XMR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XPB" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XPM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XRP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XSI" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XST" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XSV" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XUSD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XVC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "XXC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YACC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YANG" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "YIN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZEC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "BTC", + "ZRX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "BTC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "BCH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "DASH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "ETC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "ETH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "LTC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "NXT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "REP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "STR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "XMR" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "XRP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "USDT", + "ZEC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "CVC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ETC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "BCH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "GAS" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "GNO" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "GNT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "LSK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "OMG" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "REP" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "STEEM" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ZEC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "ETH", + "ZRX" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "BCN" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "BLK" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "BTCD" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "DASH" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "LTC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "MAID" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "NXT" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + }, + { + "pair": [ + "XMR", + "ZEC" + ], + "maxDecimals": 8, + "minimalOrder": { + "amount": 0.0001, + "unit": "asset" + } + } + ] +} \ No newline at end of file diff --git a/Server/Exchange/exchangeUtils.js b/Server/Exchange/exchangeUtils.js new file mode 100644 index 000000000..853d6d825 --- /dev/null +++ b/Server/Exchange/exchangeUtils.js @@ -0,0 +1,100 @@ +// generic low level reusuable utils for interacting with exchanges. + +const retry = require('retry'); +const _ = require('lodash'); + +const retryInstance = (options, checkFn, callback) => { + if(!options) { + options = { + retries: 100, + factor: 1.2, + minTimeout: 1 * 1000, + maxTimeout: 4 * 1000 + }; + } + + let attempt = 0; + + const operation = retry.operation(options); + operation.attempt(function(currentAttempt) { + checkFn((err, result) => { + if (!err) { + return callback(global.DEFAULT_OK_RESPONSE, result); + } else { + if (err.result === global.CUSTOM_OK_RESPONSE.result) { + return callback(err, result); + } + } + + let maxAttempts = err.retry; + if(maxAttempts === true) + maxAttempts = 10; + + if(err.retry && attempt++ < maxAttempts) { + return operation.retry(err); + } + + if(err.notFatal) { + if(err.backoffDelay) { + return setTimeout(() => operation.retry(err), err.backoffDelay); + } + + return operation.retry(err); + } + + callback(err, result); + }); + }); +} + +const isValidOrder = ({api, market, amount, price}) => { + let reason = false; + + // Check amount + if(amount < market.minimalOrder.amount) { + reason = 'Amount is too small'; + } + + // Some exchanges have restrictions on prices + if( + _.isFunction(api.isValidPrice) && + !api.isValidPrice(price) + ) { + reason = 'Price is not valid'; + } + + if( + _.isFunction(api.isValidLot) && + !api.isValidLot(price, amount) + ) { + reason = 'Lot size is too small'; + } + + return { + reason, + valid: !reason + } +} + +const includes = function(str, list) { + if (!_.isString(str)) + return false; + + return _.some(list, item => str.includes(item)); +} + + +const getMarketConfig = function(exchangeProperties) { + return _.find(exchangeProperties.markets, (p) => { + return _.first(p.pair) === global.MARKET.assetA.toUpperCase() && + _.last(p.pair) === global.MARKET.assetB.toUpperCase(); + }); +} + +module.exports = { + retry: retryInstance, + includes: includes, + isValidOrder, + getMarketConfig + +} \ No newline at end of file diff --git a/Server/Github.js b/Server/Github.js new file mode 100644 index 000000000..af9dac3b7 --- /dev/null +++ b/Server/Github.js @@ -0,0 +1,108 @@ +exports.newGithub = function newGithub() { + + let thisObject = { + getGithubData: getGithubData, + initialize: initialize + } + + let githubData; + + return thisObject; + + function initialize(pGithubData) { + + githubData = pGithubData; + + } + + function getGithubData(pOrg, pRepo, pPath, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Github -> getGithubData -> Entering function."); } + + let cacheVersion = githubData.get(pOrg + '.' + pRepo + '.' + pPath) + + if (cacheVersion !== undefined) { + + if (CONSOLE_LOG === true) { console.log("[INFO] Github -> getGithubData -> " + pOrg + '.' + pRepo + '.' + pPath + " found at cache."); } + + callBackFunction(cacheVersion); + + } else { + + if (CONSOLE_LOG === true) { console.log("[INFO] Github -> getGithubData -> " + pOrg + '.' + pRepo + '.' + pPath + " NOT found at cache."); } + + const octokit = require('@octokit/rest')() + global.atob = require("atob"); + + let owner = pOrg; + let repo = pRepo; + let branch = "master"; + let page = 1; + let per_page = 100; + let ref = "master"; + let path = pPath; + + octokit.repos.getContent({ owner, repo, path, ref }, onContent); + + function onContent(error, result) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Github -> getGithubData -> onContent -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Github -> getGithubData -> onContent -> error = " + error); } + if (CONSOLE_LOG === true) { console.log("[INFO] Github -> getGithubData -> onContent -> Github.com responded to request " + pOrg + '.' + pRepo + '.' + pPath + " with result = " + result.toString().substring(0, 100)); } + + if (error !== null) { console.log("[ERROR] Github -> getGithubData -> onContent -> " + error); } + + let decoded = atob(result.data.content); + + /* + + This method usually brings up to 3 characters of encoding info at the begining of the JSON string which destroys the JSON format. + We will run the following code with the intention to eliminate this problem. + + */ + + let cleanString = decoded; + let jsonTest; + + try { + jsonTest = JSON.parse(cleanString); + } catch (err) { + cleanString = decoded.substring(1); + try { + jsonTest = JSON.parse(cleanString); + } catch (err) { + cleanString = decoded.substring(2); + try { + jsonTest = JSON.parse(cleanString); + } catch (err) { + cleanString = decoded.substring(3); + try { + jsonTest = JSON.parse(cleanString); + } catch (err) { + console.log("[INFO] Github -> getGithubData -> onContent -> Could not clean the data received -> Data = " + decoded.substring(0, 50)); + } + } + } + } + + githubData.set(pOrg + '.' + pRepo + '.' + pPath, cleanString); + + callBackFunction(cleanString); + + } catch (err) { + console.log("[ERROR] Github -> getGithubData -> onContent -> err.message = " + err.message); + callBackFunction("{}"); + } + } + } + + } catch (err) { + console.log("[ERROR] Github -> getGithubData -> err.message = " + err.message); + callBackFunction("{}"); + } + } +} \ No newline at end of file diff --git a/Server/SessionManager.js b/Server/SessionManager.js new file mode 100644 index 000000000..73a05e60b --- /dev/null +++ b/Server/SessionManager.js @@ -0,0 +1,65 @@ +exports.newSessionManager = function newSessionManager() { + + let thisObject = { + getSession: getSession, + initialize: initialize + } + + let activeSessions = new Map(); + + return thisObject; + + function initialize(pServerConfig, callBackFunction) { + + readSessions(); + + function readSessions() { + + try { + + const STORAGE = require('../Server/Storage'); + let storage = STORAGE.newStorage(); + + storage.initialize(undefined, pServerConfig); + storage.readData("AdvancedAlgos", "AAPlatform", "open.sessions.json", false, onData); + + function onData(err, pText) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] SessionManager -> initialize -> readSessions -> onData -> Could not read a file. "); + console.log("[ERROR] SessionManager -> initialize -> readSessions -> onData -> err.message = " + err.message); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + let sessions = JSON.parse(pText); + + for (let i = 0; i < sessions.length; i++) { + + let session = sessions[i]; + + activeSessions.set(session.sessionToken, session); + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } + } + catch (err) { + console.log("[ERROR] readSessions -> err = " + err.message); + console.log("[HINT] You need to have a file at this path -> " + filePath); + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + function getSession(pSessionToken) { + + return activeSessions.get(pSessionToken); + + } +} \ No newline at end of file diff --git a/Server/Storage.js b/Server/Storage.js new file mode 100644 index 000000000..f094c0fc7 --- /dev/null +++ b/Server/Storage.js @@ -0,0 +1,392 @@ +exports.newStorage = function newStorage() { + + let thisObject = { + readData: readData, + writeData: writeData, + createContainer: createContainer, + deleteContainer: deleteContainer, + deleteBlob: deleteBlob, + initialize: initialize + } + + let storageData; + let serverConfig; + + return thisObject; + + function initialize(pStorageData, pServerConfig) { + + storageData = pStorageData; + serverConfig = pServerConfig; + + } + + function readData(pOrg, pPath, pFile, saveAtCache, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> pOrg = " + pOrg); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> pPath = " + pPath); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> pFile = " + pFile); } + + let cacheVersion; + + if (storageData !== undefined) { + + cacheVersion = storageData.get(pOrg + '.' + pPath + '.' + pFile); + } + + if (cacheVersion !== undefined) { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> " + pOrg + '.' + pPath + '.' + pFile + " found at cache."); } + + callBackFunction(global.DEFAULT_OK_RESPONSE, cacheVersion); + + } else { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> " + pOrg + '.' + pPath + '.' + pFile + " NOT found at cache."); } + + let storage = require('azure-storage'); + let connectionString; + + switch (serverConfig.environment) { + + case "Develop": { + + connectionString = serverConfig.configAndPlugins.Develop.connectionString; + break; + } + + case "Production": { + + connectionString = serverConfig.configAndPlugins.Production.connectionString; + break; + } + } + + let blobService = storage.createBlobService(connectionString); + + blobService.getBlobToText('aaplatform', pOrg + "/" + pPath + "/" + pFile, onFileReceived); + + + function onFileReceived(err, text, response) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> onFileReceived -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> onFileReceived -> err = " + JSON.stringify(err)); } + if (LOG_FILE_CONTENT === true) { console.log("[INFO] Storage -> readData -> onFileReceived -> response = " + JSON.stringify(response)); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> onFileReceived -> pOrg = " + pOrg); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> onFileReceived -> pPath = " + pPath); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> readData -> onFileReceived -> pFile = " + pFile); } + + if (saveAtCache === true) { + + storageData.set(pOrg + '.' + pPath + '.' + pFile, text); + + } + + if (err !== null || text === null) { + + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> readData -> onFileReceived -> Error Received from Storage Library. "); } + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> readData -> onFileReceived -> err = " + JSON.stringify(err)); } + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> readData -> onFileReceived -> Returning an empty JSON object string. "); } + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE, text); + + } catch (err) { + console.log("[ERROR] Storage -> readData -> onFileReceived -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + } + + } catch (err) { + console.log("[ERROR] Storage -> readData -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function writeData(pOrg, pPath, pFile, pFileContent, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> pOrg = " + pOrg); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> pPath = " + pPath); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> pFile = " + pFile); } + + let storage = require('azure-storage'); + let connectionString; + + switch (serverConfig.environment) { + + case "Develop": { + + connectionString = serverConfig.configAndPlugins.Develop.connectionString; + break; + } + + case "Production": { + + connectionString = serverConfig.configAndPlugins.Production.connectionString; + break; + } + } + + let blobService = storage.createBlobService(connectionString); + let blobPath = pOrg + "/" + pPath + "/" + pFile; + let blobText = pFileContent.toString(); + + blobService.createBlockBlobFromText('aaplatform', blobPath, blobText, onFileCreated); + + function onFileCreated(err, text, response) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> onFileCreated -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> onFileCreated -> err = " + JSON.stringify(err)); } + if (LOG_FILE_CONTENT === true) { console.log("[INFO] Storage -> writeData -> onFileCreated -> response = " + JSON.stringify(response)); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> onFileCreated -> pOrg = " + pOrg); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> onFileCreated -> pPath = " + pPath); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> writeData -> onFileCreated -> pFile = " + pFile); } + + if (err !== null || text === null) { + + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> writeData -> onFileCreated -> Error Received from Storage Library. "); } + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> writeData -> onFileCreated -> err = " + JSON.stringify(err)); } + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + console.log("[ERROR] Storage -> writeData -> onFileCreated -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + console.log("[ERROR] Storage -> writeData -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function deleteBlob(pOrg, pPath, pFile, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> pOrg = " + pOrg); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> pPath = " + pPath); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> pFile = " + pFile); } + + let storage = require('azure-storage'); + let connectionString; + + switch (serverConfig.environment) { + + case "Develop": { + + connectionString = serverConfig.configAndPlugins.Develop.connectionString; + break; + } + + case "Production": { + + connectionString = serverConfig.configAndPlugins.Production.connectionString; + break; + } + } + + let blobService = storage.createBlobService(connectionString); + let blobPath = pOrg + "/" + pPath + "/" + pFile; + + blobService.deleteBlob('aaplatform', blobPath, onBlobDeleted); + + function onBlobDeleted(err, text, response) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> onBlobDeleted -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> onBlobDeleted -> err = " + JSON.stringify(err)); } + if (LOG_FILE_CONTENT === true) { console.log("[INFO] Storage -> deleteBlob -> onBlobDeleted -> response = " + JSON.stringify(response)); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> onBlobDeleted -> pOrg = " + pOrg); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> onBlobDeleted -> pPath = " + pPath); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteBlob -> onBlobDeleted -> pFile = " + pFile); } + + if (err !== null || text === null) { + + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> deleteBlob -> onBlobDeleted -> Error Received from Storage Library. "); } + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> deleteBlob -> onBlobDeleted -> err = " + JSON.stringify(err)); } + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + + } catch (err) { + console.log("[ERROR] Storage -> deleteBlob -> onBlobDeleted -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + } catch (err) { + console.log("[ERROR] Storage -> deleteBlob -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function createContainer(pContainerName, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> createContainer -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> createContainer -> pContainerName = " + pContainerName); } + + let storage = require('azure-storage'); + let connectionString; + + switch (serverConfig.environment) { + + case "Develop": { + + connectionString = serverConfig.configAndPlugins.Develop.connectionString; + break; + } + + case "Production": { + + connectionString = serverConfig.configAndPlugins.Production.connectionString; + break; + } + } + + let blobService = storage.createBlobService(connectionString); + + let containerName = pContainerName.toLowerCase(); + + blobService.createContainer(containerName, onContainerCreated); + + function onContainerCreated(err) { + + if (err) { + + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> createContainer -> onContainerCreated -> Error Received from Storage Library. "); } + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> createContainer -> onContainerCreated -> err = " + JSON.stringify(err)); } + + + /* ContainerAlreadyExists check */ + + if (JSON.stringify(err).indexOf("ContainerAlreadyExists") > 0) { + + let err = { + resutl: global.CUSTOM_FAIL_RESPONSE.result, + message: "ContainerAlreadyExists" + }; + + callBackFunction(err); + return; + + } else { + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + + } catch (err) { + console.log("[ERROR] Storage -> createContainer -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } + + function deleteContainer(pContainerName, callBackFunction) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteContainer -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] Storage -> deleteContainer -> pContainerName = " + pContainerName); } + + let storage = require('azure-storage'); + let connectionString; + + switch (serverConfig.environment) { + + case "Develop": { + + connectionString = serverConfig.configAndPlugins.Develop.connectionString; + break; + } + + case "Production": { + + connectionString = serverConfig.configAndPlugins.Production.connectionString; + break; + } + } + + let blobService = storage.createBlobService(connectionString); + + let containerName = pContainerName.toLowerCase(); + + blobService.deleteContainer(containerName, onContainerDeleted); + + function onContainerDeleted(err) { + + if (err) { + + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> deleteContainer -> onContainerDeleted -> Error Received from Storage Library. "); } + if (CONSOLE_ERROR_LOG === true) { console.log("[ERROR] Storage -> deleteContainer -> onContainerDeleted -> err = " + JSON.stringify(err)); } + + + /* ContainerAlreadyExists check */ + + if (JSON.stringify(err).indexOf("ContainerNotFound") > 0) { + + let err = { + resutl: global.CUSTOM_FAIL_RESPONSE.result, + message: "ContainerNotFound" + }; + + callBackFunction(err); + return; + + } else { + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + return; + } + + callBackFunction(global.DEFAULT_OK_RESPONSE); + return; + } + + } catch (err) { + console.log("[ERROR] Storage -> deleteContainer -> err.message = " + err.message); + callBackFunction(global.DEFAULT_FAIL_RESPONSE); + } + } +} \ No newline at end of file diff --git a/Server/StorageAccessManager.js b/Server/StorageAccessManager.js new file mode 100644 index 000000000..3980a259a --- /dev/null +++ b/Server/StorageAccessManager.js @@ -0,0 +1,81 @@ +exports.newStorageAccessManager = function newStorageAccessManager() { + + let thisObject = { + getPermission: getPermission, + initialize: initialize + } + + let permissions; + + return thisObject; + + function initialize(callBackFunction) { + + readStoragePermissions(); + + function readStoragePermissions() { + + let filePath; + + try { + let fs = require('fs'); + filePath = './' + 'Storage' + '/' + 'Storage.Permissions.json'; + permissions = JSON.parse(fs.readFileSync(filePath, 'utf8')); + + callBackFunction(); + + } + catch (err) { + console.log("[ERROR] readStoragePermissions -> err = " + err.message); + console.log("[HINT] readStoragePermissions -> You need to have a file at this path -> " + filePath); + + callBackFunction(); + } + } + } + + function getPermission(pContainer, pType, pDays) { + + let azure = require('azure-storage'); + let blobService = azure.createBlobService(permissions.connectionString); + + let startDate = new Date(); + startDate.setDate(startDate.getDate() - 1); + + let expiryDate = new Date(startDate); + expiryDate.setDate(startDate.getDate() + pDays); + + let azurePermissions; + + switch (pType) { + case "READ": { + azurePermissions = azure.BlobUtilities.SharedAccessPermissions.READ; + break; + } + case "WRITE": { + azurePermissions = azure.BlobUtilities.SharedAccessPermissions.WRITE; + break; + } + case "DELETE": { + azurePermissions = azure.BlobUtilities.SharedAccessPermissions.DELETE; + break; + } + } + + let blobName = undefined; + + let sharedAccessPolicy = { + AccessPolicy: { + Permissions: azurePermissions, + Start: startDate, + Expiry: expiryDate + } + }; + + let sasToken = blobService.generateSharedAccessSignature(pContainer, blobName, sharedAccessPolicy); + + return "BlobEndpoint=https://aadevelop.blob.core.windows.net;SharedAccessSignature=" + sasToken + ""; + + } +} + diff --git a/Spaces/ChartSpace.js b/Spaces/ChartSpace.js deleted file mode 100644 index 81a441fe0..000000000 --- a/Spaces/ChartSpace.js +++ /dev/null @@ -1,121 +0,0 @@ -/* - -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 newChartSpace() { - - const MODULE_NAME = "Chart Space"; - const INFO_LOG = false; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - var chartSpace = { - 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(); - chartSpace.container = container; - - chartSpace.container.frame.width = browserCanvas.width * 1000; - chartSpace.container.frame.height = browserCanvas.height * 100; - - container.displacement.containerName = "Chart Space"; - container.frame.containerName = "Chart Space"; - - container.frame.position.x = browserCanvas.width / 2 - chartSpace.container.frame.width / 2; - container.frame.position.y = browserCanvas.height / 2 - chartSpace.container.frame.height / 2; - - container.isDraggeable = false; - - return chartSpace; - - function initialize() { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - /* We create the first of many possible time machines that could live at the Chart Space. */ - - var timeMachine = newTimeMachine(); - - timeMachine.container.displacement.parentDisplacement = this.container.displacement; - timeMachine.container.frame.parentFrame = this.container.frame; - - timeMachine.container.parentContainer = this.container; - - /* We make the time machine a little bit smaller than the current space. */ - - timeMachine.container.frame.width = this.container.frame.width * 1; - timeMachine.container.frame.height = this.container.frame.height * 1; - - timeMachine.container.frame.position.x = this.container.frame.width / 2 - timeMachine.container.frame.width / 2; - timeMachine.container.frame.position.y = this.container.frame.height / 2 - timeMachine.container.frame.height / 2; - - timeMachine.initialize(); - - this.timeMachines.push(timeMachine); - - } - - function draw() { - - chartSpace.container.frame.draw(false, false); - - /* When we draw the chart space, that means we draw all the time machines in it. */ - - for (var i = 0; i < chartSpace.timeMachines.length; i++ ) { - - var timeMachine = chartSpace.timeMachines[i]; - timeMachine.draw(); - - } - - } - - function getContainer(point) { - - if (INFO_LOG === true) { logger.write("[INFO] getContainer -> Entering function."); } - - var container; - - /* First we check if this point is inside this space. */ - - if (this.container.frame.isThisPointHere(point) === true) { - - /* Now we see which is the inner most container that has it */ - - for (var i = 0; i < this.timeMachines.length; i++) { - - container = this.timeMachines[i].getContainer(point); - - if (container !== undefined) { - - /* We found an inner container which has the point. We return it. */ - - return container; - } - } - - /* The point does not belong to any inner container, so we return the current container. */ - - return this.container; - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - - } - -} \ No newline at end of file diff --git a/Spaces/FloatingSpace.js b/Spaces/FloatingSpace.js deleted file mode 100644 index a1d20901d..000000000 --- a/Spaces/FloatingSpace.js +++ /dev/null @@ -1,44 +0,0 @@ - -function newFloatingSpace() { - - /* - 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 - profileBalls: undefined, - noteSets: undefined, - initialize: initialize - }; - - return thisObject; - - function initialize(callBackFunction) { - - thisObject.floatingLayer = newFloatingLayer(); - thisObject.floatingLayer.initialize(onFloatingLayerInitialized); - - function onFloatingLayerInitialized(err) { - - thisObject.profileBalls = newProfileBalls(); - thisObject.profileBalls.initialize(thisObject.floatingLayer, onProfileBallsInitialized); - - function onProfileBallsInitialized(err) { - - thisObject.noteSets = newNoteSets(); - thisObject.noteSets.initialize(thisObject.floatingLayer, onNoteSetsInitialized); - - function onNoteSetsInitialized(err) { - - - - - } - } - } - } -} - - diff --git a/Spaces/PanelsSpace.js b/Spaces/PanelsSpace.js deleted file mode 100644 index f2adebdd0..000000000 --- a/Spaces/PanelsSpace.js +++ /dev/null @@ -1,137 +0,0 @@ -/* - -The Panel Space y the place wehre all panels live, no matter who create them. - -*/ - -function newPanelsSpace() { - - var thisObject = { - container: undefined, - createNewPanel: createNewPanel, - destroyPanel: destroyPanel, - getPanel: getPanel, - draw: draw, - panels: [], - getContainer: getContainer, // returns the inner most container that holds the point received by parameter. - initialize: initialize - }; - - var container = newContainer(); - container.initialize(); - thisObject.container = container; - thisObject.container.isDraggeable = false; - - container.displacement.containerName = "Panels Space"; - 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) { - - let panel; - - switch (pType) { - - case "Time Control Panel": - { - panel = newTimeControlPanel(); - panel.initialize(); - break; - } - - case "Products Panel": - { - panel = newProductsPanel(); - panel.initialize(); - break; - } - case "Plotter Panel": - { - panel = getNewPlotterPanel(pParameters.devTeam, pParameters.plotterCodeName, pParameters.moduleCodeName, pParameters.panelCodeName); - panel.initialize(); - break; - } - } - - thisObject.panels.push(panel); - - panel.handle = Math.floor((Math.random() * 10000000) + 1); - - return panel.handle; - } - - function destroyPanel(pPanelHandle) { - - for (let i = 0; i < thisObject.panels.length; i++) { - - let panel = thisObject.panels[i]; - - if (panel.handle === pPanelHandle) { - 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 draw() { - - thisObject.container.frame.draw(false, false); - - /* When we draw a time machine, that means also to draw all the charts in it. */ - - for (var i = 0; i < thisObject.panels.length; i++) { - - let panel = thisObject.panels[i]; - panel.draw(); - - } - } - - function getContainer(point) { - - 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 (var i = thisObject.panels.length - 1; i >= 0; i--) { - - container = thisObject.panels[i].getContainer(point); - - if (container !== undefined) { - - /* We found an inner container which has the point. We return it. */ - - return container; - } - } - - /* The point does not belong to any inner container, so we return the current container. */ - - return thisObject.container; - - } -} \ No newline at end of file diff --git a/SplashScreen.js b/SplashScreen.js deleted file mode 100644 index b8fb64aaa..000000000 --- a/SplashScreen.js +++ /dev/null @@ -1,176 +0,0 @@ -/* - -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() { - - var splashScreen = { - 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(); - splashScreen.container = container; - - splashScreen.container.frame.width = browserCanvas.width; - splashScreen.container.frame.height = browserCanvas.height; - - container.displacement.containerName = "Splash Screen"; - container.frame.containerName = "Splash Screen"; - - container.frame.position.x = 0; - container.frame.position.y = 0; - - container.isDraggeable = false; - - - let fadeOutCounter = 0; - let opacity = 1; - - return splashScreen; - - function initialize() { - - - - } - - function draw() { - - if (splashScreenNeeded === false) { - - stepsInitializationCounter = 100; - - fadeOutCounter++; - - if (fadeOutCounter > 20) { - return; - } - - opacity = opacity - 0.05; - - } - - splashScreen.container.frame.draw(false, false); - - let label = '' + Math.trunc(stepsInitializationCounter) + ' %'; - stepsInitializationCounter = stepsInitializationCounter + 1; - - if (stepsInitializationCounter > 99) { - - splashScreenNeeded = false; - stepsInitializationCounter = 99; - } - - let fontSize = 50; - - let labelPoint = { - x: splashScreen.container.frame.width / 2 - label.length / 2 * fontSize * FONT_ASPECT_RATIO -30, - y: splashScreen.container.frame.height / 2 + fontSize / 2 + fontSize * 0.1 + 200 - }; - - browserCanvasContext.beginPath(); - - browserCanvasContext.rect(0, 0, splashScreen.container.frame.width, splashScreen.container.frame.height); - browserCanvasContext.fillStyle = 'rgba(255, 255, 255, ' + opacity + ')'; - - browserCanvasContext.closePath(); - - browserCanvasContext.fill(); - - browserCanvasContext.font = fontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(200, 200, 200, ' + opacity + ')'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - let title; - let titlePoint; - - /* Advanced Algos Title */ - - fontSize = 100; - - title = "Advanced" - - titlePoint = { - x: splashScreen.container.frame.width / 2 - title.length / 2 * fontSize * FONT_ASPECT_RATIO - 230, - y: splashScreen.container.frame.height / 2 - fontSize * 2 + 50 - }; - - browserCanvasContext.font = fontSize + 'px Verdana'; - browserCanvasContext.fillStyle = 'rgba(0, 0, 0, ' + opacity + ')'; - browserCanvasContext.fillText(title, titlePoint.x, titlePoint.y); - - title = "A" - - browserCanvasContext.fillStyle = 'rgba(255, 0, 0, ' + opacity + ')'; - browserCanvasContext.fillText(title, titlePoint.x, titlePoint.y); - - title = "Algos" - - titlePoint = { - x: splashScreen.container.frame.width / 2 - title.length / 2 * fontSize * FONT_ASPECT_RATIO + 230, - y: splashScreen.container.frame.height / 2 - fontSize * 2 + 50 - }; - - browserCanvasContext.font = fontSize + 'px Verdana'; - browserCanvasContext.fillStyle = 'rgba(0, 0, 0, ' + opacity + ')'; - browserCanvasContext.fillText(title, titlePoint.x, titlePoint.y); - - title = "A" - - browserCanvasContext.fillStyle = 'rgba(255, 0, 0, ' + opacity + ')'; - browserCanvasContext.fillText(title, titlePoint.x, titlePoint.y); - - /* Algo Bots Competitions Sub Title */ - - fontSize = 50; - - title = "Algobots Competitions" - - titlePoint = { - x: splashScreen.container.frame.width / 2 - title.length / 2 * fontSize * FONT_ASPECT_RATIO + 0, - y: splashScreen.container.frame.height / 2 - fontSize * 2 + 150 - }; - - browserCanvasContext.font = fontSize + 'px Verdana'; - 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 (this.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 this.container; - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - - } - -} \ No newline at end of file diff --git a/TempExosystem.js b/TempExosystem.js deleted file mode 100644 index 53d0ba6a0..000000000 --- a/TempExosystem.js +++ /dev/null @@ -1,322 +0,0 @@ -{ - codeName: "AACharly", - displayName: "Charly", - type: "Extract", - version: { - number: "1.0.0", - status: "Mantained", // Discontinued - }, - products: [{ - - product: { - codeName: "Trades", - displayName: "Trades", - description: "Each file contains trades records of one market at one exchange in a period of time of one minute.", - storageAccount: "aacharly", - filePath: "@Exchange/Output/Trades/@Year/@Month/@Date/@Hour/@Minute", - fileName: "@AssetA_@AssetB.json", - exchangeList: [{ - exchange: { - name: "Poloniex", - datetimeRange: { - minDatetime: "2017-02-19 19:15:00.000 GMT+0000", - maxDatetime: "2018-02-13 10:10:00.000 GMT+0000" - } - } - }] - , - layers: [{ - layer: { - codeName: "Trades", - displayName: "Trades", - exchangeList: ["Poloniex"], - validPeriods: ["01-min"], - plotter: { - devTeam: "AAMasters", - repo: "AACharly-Plotter", - moduleName: "TradeList", - target: "Timeline" - } - } - }] - } - }] - , - processes: - [{ - process: { - name: "Poloniex-Live-Trades", - description: "Retrieves the trades done at the current minute and the previous minute and saves them at the storage account." - } - }, { - process: { - name: "Poloniex-Historic-Trades", - description: "Retrieves and saves the historical trades in batches going backwards from the current time until reaching the begining of the market." - } - }, { - process: { - name: "Poloniex-Hole-Fixing", - description: "Scans the trades saved by the Live Trades and Historic Trades processes searching for missing records. Once a hole is found on the data set, it patches it by retrieving the missing records from the exchange." - } - }] - , - directDependencies: { }, - directDependents: [{ - bot: { - devTeam: "AAMasters", - codeName: "AABruce" - } - }, { - bot: { - devTeam: "AAMasters", - codeName: "AAOlivia" - } - }] - , - collaboratorsList: [], - github: { - org: "AAMasters", - repos: [{ - repo: { - name: "AACharly-Bot", - description: "Produces a data-set of all trades of every market in multiple exchanges.", - openIssues: 0, - openPullRequests: 0, - contributors: 1 - } - }, { - repo: { - name: "AACharly-Plotter", - description: "Plotters plug-ins to browse trades at the AA Web Platform.", - openIssues: 0, - openPullRequests: 0, - contributors: 1 - } - }] - }, - storage: { - sas: '?sv=2017-04-17&ss=f&srt=sco&sp=rc&se=2018-12-31T02:33:36Z&st=2018-02-01T18:33:36Z&spr=https&sig=xFTcopdKq0f60wl2iuukbtIpcLwxZVKzlw5BzpJfo5g%3D', - fileUri: 'https://' + 'aacharly' + '.file.core.windows.net' - } -}, -{ - codeName: "AACarol", - displayName: "Carol", - type: "Extract", - version: { - number: "1.0.0", - status: "Mantained", // Discontinued - }, - products: [ - { - product: { - codeName: "Order Book", - displayName: "Order Book", - description: "Each file contains the bids and asks positions at each market at a certain moment in time.", - storageAccount: "aacarol", - filePath: "@Exchange/Output/Order-Books/@Year/@Month/@Date/@Hour/@Minute", - fileName: "@AssetA_@AssetB.json", - exchangeList: [{ - exchange: { - name: "Poloniex", - datetimeRange: { - minDatetime: "2017-02-19 19:15:00.000 GMT+0000", - maxDatetime: "2018-02-13 10:10:00.000 GMT+0000" - } - } - }], - layers: { - layer: { - codeName: "Order Book", - displayName: "Order Book", - exchangeList: ["Poloniex"], - validPeriods: ["06-hs", "03-hs", "01-hs", "30-min", "10-min", "05-min", "01-min"], - plotter: { - devTeam: "AAMasters", - repo: "AACarol-Plotter", - moduleName: "OrderBook", - target: "Panel" - } - } - } - } - }, - { - product: { - codeName: "Order Books", - displayName: "Order Books", - description: "Each file contains the sumarized bids and asks positions at each market at a certain moment in time. Agreggation makes the dataset optimal for different time periods.", - storageAccount: "aacarol", - filePath: "@Exchange/Output/Aggregated-Order-Books/@Period/@Year/@Month/@Date/@Hour/@Minute", - fileName: "@AssetA_@AssetB.json", - exchangeList: [{ - exchange: { - name: "Poloniex", - datetimeRange: { - minDatetime: "2017-02-19 19:15:00.000 GMT+0000", - maxDatetime: "2018-02-13 10:10:00.000 GMT+0000" - } - } - }], - layers: { - layer: { - codeName: "Order Books", - displayName: "Order Books", - exchangeList: ["Poloniex"], - validPeriods: ["06-hs", "03-hs", "01-hs", "30-min", "10-min", "05-min", "01-min"], - plotter: { - devTeam: "AAMasters", - repo: "AACarol-Plotter", - moduleName: "OrderBooks", - target: "Timeline" - } - } - } - } - } - ], - processes: [{ - process: { - name: "Poloniex-Order-Books", - description: "Retrieves the order books at the current minute and saves them at the storage account." - } - }, { - process: { - name: "Aggregated-Order-Books", - description: "Aggregates the order books information to be viewed at different time periods." - } - } - ], - directDependencies: [], - directDependents: [], - collaboratorsList: [], - github: { - org: "AAMasters", - repos: [{ - repo: { - name: "AACarol", - description: "Produces a data-set with a snapshot of all markets order books every minute.", - openIssues: 0, - openPullRequests: 0, - contributors: 1 - } - } - ] - }, - storage: { - sas: '?sv=2017-04-17&ss=f&srt=sco&sp=r&se=2018-12-01T00:09:44Z&st=2017-12-01T13:09:44Z&spr=https,http&sig=J60UlQuUqh9Hd1Pm0PqIF6yari3D3TkGEz1fSNQJWks%3D', - fileUri: 'https://' + 'aadata' + '.file.core.windows.net' - } -} -{ - codeName: "AABruce", - displayName: "Bruce", - type: "Indicator", - version: { - number: "1.0.0", - status: "Mantained", // Discontinued - }, - products: { - product: [{ - codeName: "Candles", - displayName: "One Minute Candles in Daily Files", - description: "Each file contains the candles of one whole day on a one minute time period.", - storageAccount: "aabruce", - filePath: "@Exchange/Output/Candles/One-Min/@Year/@Month/@Date", - fileName: "@AssetA_@AssetB.json", - exchangeList: [{ - exchange: { - name: "Poloniex", - datetimeRange: { - minDatetime: "2017-02-19 19:15:00.000 GMT+0000", - maxDatetime: "2018-02-13 10:10:00.000 GMT+0000" - } - } - }], - layers: { - layer: { - codeName: "Candles", - displayName: "Candlesticks", - exchangeList: ["Poloniex"], - validPeriods: ["30-min", "10-min", "05-min", "01-min"], - plotter: { - devTeam: "AAMasters", - repo: "AABruce-Plotter", - moduleName: "Candlesticks", - target: "Timeline", - dependencies: { - dependency: { - devTeam: "AAMasters", - repo: "Plotters-Utilities", - moduleName: "Candlesticks" - } - } - } - } - } - }, { - codeName: "Volumes", - displayName: "One Minute Volumes in Daily Files", - description: "Each file contains the volumes of one whole day on a one minute time period.", - storageAccount: "aabruce", - filePath: "@Exchange/Output/Volumes/One-Min/@Year/@Month/@Date", - fileName: "@AssetA_@AssetB.json", - exchangeList: [{ - exchange: { - name: "Poloniex", - datetimeRange: { - minDatetime: "2017-02-19 19:15:00.000 GMT+0000", - maxDatetime: "2018-02-13 10:10:00.000 GMT+0000" - } - } - }], - layers: { - layer: { - codeName: "Volumes", - displayName: "Sell and Buy Volumes", - exchangeList: ["Poloniex"], - validPeriods: ["06-hs", "03-hs", "01-hs", "30-min", "10-min", "05-min", "01-min"], - plotter: { - devTeam: "AAMasters", - repo: "AABruce-Plotter", - moduleName: "Volumes", - target: "Timeline", - dependencies: { - dependency: { - devTeam: "AAMasters", - repo: "Plotters-Utilities", - moduleName: "Volumes" - } - } - } - } - } - } - ] - , - processes: - { - process: [{ - name: "One-Min-Daily-Candles-Volumes", - description: "Read trades files and generates one minute candles and volumes files for each day." - }] - }, - directDependencies: [], - directDependents: [], - collaboratorsList: [], - github: { - org: "AAMasters", - repos: { - repo: [{ - name: "AABruce", - description: "Produces two data-sets with candles and volumes for all markets at all exchanges.", - openIssues: 0, - openPullRequests: 0, - contributors: 1 - }] - } - }, - storage: { - sas: '?sv=2017-04-17&ss=f&srt=sco&sp=r&se=2018-12-31T02:32:04Z&st=2018-02-01T18:32:04Z&spr=https&sig=SbZI6bvAE1MBxna2NOvfgMroPFZNjAyaHUsAPvh11hg%3D', - fileUri: 'https://' + 'aabruce' + '.file.core.windows.net' - } \ No newline at end of file diff --git a/TextButton.js b/TextButton.js deleted file mode 100644 index 61a92ed85..000000000 --- a/TextButton.js +++ /dev/null @@ -1,140 +0,0 @@ -function newTextButton() { - - var textButton = { - container: undefined, - draw: draw, - status: 'off', - type: undefined, - getContainer: getContainer, // returns the inner most container that holds the point received by parameter. - initialize: initialize - }; - - var container = newContainer(); - container.initialize(); - container.isDraggeable = false; - container.isClickeable = true; - textButton.container = container; - - return textButton; - - function initialize() { - - /* Lets set the basic dimensions of this textButton. */ - - var position = { - x: 0, - y: 0 - }; - - this.container.frame.position = position; - this.container.frame.width = 180; - this.container.frame.height = 14; - - /* Now we start listening to the onClick event from the mouse, so that we can allow users operate this textButton. */ - - - } - - function getContainer(point) { - - var container; - - /* First we check if this point is inside this space. */ - - if (this.container.frame.isThisPointHere(point, true) === true) { - - return this.container; - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - - } - - - - function draw() { - - drawIcon(); - - } - - - function drawIcon() { - - centerPoint = { - x: 7, - y: 7 - }; - - /* Now the transformations. */ - - centerPoint = textButton.container.frame.frameThisPoint(centerPoint); - - /* Lets start the drawing. */ - - browserCanvasContext.beginPath(); - browserCanvasContext.arc(centerPoint.x, centerPoint.y, 3, 0, 2 * Math.PI); - browserCanvasContext.closePath(); - - let offFillStyle = 'rgba(232, 28, 28, 1)'; - let onFillStyle = 'rgba(45, 232, 28, 1)'; - let invisibleFillStyle = 'rgba(247, 244, 27, 1)'; - - switch (textButton.status) { - - case 'on': - browserCanvasContext.fillStyle = onFillStyle; - break; - - case 'invisible': - browserCanvasContext.fillStyle = invisibleFillStyle; - break; - - case 'off': - browserCanvasContext.fillStyle = offFillStyle; - break; - - } - - - browserCanvasContext.fill(); - - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 1)'; - browserCanvasContext.lineWidth = 1; - browserCanvasContext.stroke(); - - browserCanvasContext.closePath(); - - - /* print the text */ - - let labelPoint; - let fontSize = 10; - - browserCanvasContext.font = fontSize + 'px Courier New'; - - let label = textButton.type; - - let xOffset = 0; - let yOffset = 0; - - labelPoint = { - x: 20, - y: textButton.container.frame.height - 5 - }; - - labelPoint = textButton.container.frame.frameThisPoint(labelPoint); - - browserCanvasContext.fillStyle = 'rgba(60, 60, 60, 0.50)'; - browserCanvasContext.fillText(label, labelPoint.x, labelPoint.y); - - - } - - - -} \ No newline at end of file diff --git a/TimeLineCoordinateSystem.js b/TimeLineCoordinateSystem.js deleted file mode 100644 index 1de7389e2..000000000 --- a/TimeLineCoordinateSystem.js +++ /dev/null @@ -1,157 +0,0 @@ -/* - -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 newTimeLineCoordinateSystem() { - - var thisObject = { - min: undefined, - max: undefined, - scale: undefined, - transformThisPoint: transformThisPoint, - transformThisPoint2: transformThisPoint2, - unInverseTransform: unInverseTransform, - inverseTransformUncappedY: inverseTransformUncappedY, - initializeX: initializeX, - initializeY: initializeY, - initialize: initialize - }; - - var min = { - x: 0, - y: 0 - }; - - var max = { - x: 0, - y: 0 - }; - - var scale = { - x: 0, - y: 0 - }; - - thisObject.min = min; - thisObject.max = max; - thisObject.scale = scale; - - let maxHeight; - - return thisObject; - - function initialize(minValue, maxValue, maxWidth, 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; // * 0.999; // 0.1% less - thisObject.max.x = maxValue.x; // * 1.001; // 0.1% more - - thisObject.min.y = minValue.y; // * 0.999; // 0.1% less - thisObject.max.y = maxValue.y; // * 1.001; // 0.1% more - - /* Defines the initial Zoom level at center position. */ - - thisObject.scale.x = maxWidth / (thisObject.max.x - thisObject.min.x); - thisObject.scale.y = pMaxHeight / (thisObject.max.y - thisObject.min.y); - - maxHeight = pMaxHeight; - - } - - 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 = 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); - - 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 - this.min.x) * this.scale.x, - y: maxHeight - (point.y - this.min.y) * this.scale.y - }; - - return point; - } - - - function transformThisPoint2(point) { - - point = { - x: (point.x - this.min.x) * this.scale.x, - y: (maxHeight- point.y - this.min.y) * this.scale.y - }; - - return point; - } - - - function unInverseTransform(point, inverseY) { - - point = { - x: (point.x / this.scale.x) + this.min.x, - y: (inverseY - point.y) / this.scale.y + this.min.y - }; - - return point; - } - - - function inverseTransformUncappedY(point, inverseY) { - - point = { - x: (point.x - this.min.x) * this.scale.x, - y: (inverseY - point.y) * this.scale.y - }; - - return point; - } - - - - -} - - - - - - - - - - - diff --git a/TimeMachine.js b/TimeMachine.js deleted file mode 100644 index 6d651beb1..000000000 --- a/TimeMachine.js +++ /dev/null @@ -1,181 +0,0 @@ -/* - -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 INFO_LOG = false; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let thisObject = { - container: undefined, - draw: draw, - charts: [], - 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.displacement.containerName = "Time Machine"; - container.frame.containerName = "Time Machine"; - - let controlPanelHandle; // We need this to destroy the Panel when this object is itself destroyed or no longer needs it... - let productsPanelHandle; // ... also to request a reference to the object for the cases we need it. - - return thisObject; - - function initialize() { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - /* Each Time Machine has a Control Panel. */ - - controlPanelHandle = canvas.panelsSpace.createNewPanel("Time Control Panel"); - let controlPanel = canvas.panelsSpace.getPanel(controlPanelHandle); - - productsPanelHandle = canvas.panelsSpace.createNewPanel("Products Panel"); - let productsPanel = canvas.panelsSpace.getPanel(productsPanelHandle); - - let iteration = 0; - - /* 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 timelineChart = newTimelineChart(); - - timelineChart.container.displacement.parentDisplacement = thisObject.container.displacement; - timelineChart.container.frame.parentFrame = thisObject.container.frame; - - timelineChart.container.parentContainer = thisObject.container; - - timelineChart.container.frame.width = thisObject.container.frame.width * 1; - timelineChart.container.frame.height = thisObject.container.frame.height * 1 * CHART_ASPECT_RATIO; - - timelineChart.container.frame.position.x = thisObject.container.frame.width / 2 - timelineChart.container.frame.width / 2; - timelineChart.container.frame.position.y = timelineChart.container.frame.height * 1.5 * iteration; - - timelineChart.initialize(1, INITIAL_DEFAULT_MARKET, productsPanel, onDefaultMarketInitialized); - - iteration++; - - function onDefaultMarketInitialized() { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onDefaultMarketInitialized -> Entering function."); } - - thisObject.charts.push(timelineChart); - - controlPanel.container.eventHandler.listenToEvent('Datetime Changed', timelineChart.setDatetime, undefined); - timelineChart.container.eventHandler.listenToEvent('Datetime Changed', controlPanel.setDatetime); - - initializeTheRestOfTheMarkets(); - - } - - function initializeTheRestOfTheMarkets() { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> initializeTheRestOfTheMarkets -> Entering function."); } - - markets.forEach(initializeTimelineChart); - - function initializeTimelineChart(item, key, mapObj) { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> initializeTheRestOfTheMarkets -> initializeTimelineChart -> Entering function."); } - - if (key === INITIAL_DEFAULT_MARKET) { // We skip this market since it has already been initialized. - - return; - } - - let timelineChart = newTimelineChart(); - - timelineChart.container.displacement.parentDisplacement = thisObject.container.displacement; - timelineChart.container.frame.parentFrame = thisObject.container.frame; - - timelineChart.container.parentContainer = thisObject.container; - - timelineChart.container.frame.width = thisObject.container.frame.width * 1; - timelineChart.container.frame.height = thisObject.container.frame.height * 1 * CHART_ASPECT_RATIO; - - timelineChart.container.frame.position.x = thisObject.container.frame.width / 2 - timelineChart.container.frame.width / 2; - timelineChart.container.frame.position.y = timelineChart.container.frame.height * 1.5 * iteration; - - timelineChart.initialize(1, key, productsPanel, finalSteps); - - iteration++; - - function finalSteps() { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> initializeTheRestOfTheMarkets -> initializeTimelineChart -> finalSteps -> Entering function."); } - - thisObject.charts.push(timelineChart); - - controlPanel.container.eventHandler.listenToEvent('Datetime Changed', timelineChart.setDatetime, undefined); - timelineChart.container.eventHandler.listenToEvent('Datetime Changed', controlPanel.setDatetime); - - } - } - } - } - - function draw() { - - this.container.frame.draw(false, false); - - /* When we draw a time machine, that means also to draw all the charts in it. */ - - for (let i = 0; i < this.charts.length; i++) { - - let chart = this.charts[i]; - chart.draw(); - - } - } - - function getContainer(point) { - - if (INFO_LOG === true) { logger.write("[INFO] getContainer -> Entering function."); } - - let container; - - /* First we check if this point is inside this space. */ - - if (this.container.frame.isThisPointHere(point) === true) { - - /* Now we see which is the inner most container that has it */ - - for (let i = 0; i < this.charts.length; i++) { - - container = this.charts[i].getContainer(point); - - if (container !== undefined) { - - /* We found an inner container which has the point. We return it. */ - - return container; - } - } - - /* The point does not belong to any inner container, so we return the current container. */ - - return this.container; - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - } -} \ No newline at end of file diff --git a/TimelineChart.js b/TimelineChart.js deleted file mode 100644 index 6fd38b31c..000000000 --- a/TimelineChart.js +++ /dev/null @@ -1,1029 +0,0 @@ -function newTimelineChart() { - - const MODULE_NAME = "Timeline Chart"; - const INFO_LOG = false; - const INTENSIVE_LOG = false; - const ERROR_LOG = true; - const logger = newDebugLog(); - logger.fileName = MODULE_NAME; - - let productPlotters = []; - let competitionPlotters = []; - - let timeLineCoordinateSystem = newTimeLineCoordinateSystem(); - - let timePeriod = INITIAL_TIME_PERIOD; - let datetime = INITIAL_DATE; - - let thisObject = { - setDatetime: setDatetime, - container: undefined, - draw: draw, - getContainer: getContainer, - initialize: initialize - }; - - let container = newContainer(); - container.initialize(); - thisObject.container = container; - - container.displacement.containerName = "Time Period Chart"; - container.frame.containerName = "Time Period Chart"; - - let marketId; - let exchangeId; - - let level; - - let chartGrid; - - let initializationReady = false; - - let productsPanel; - - return thisObject; - - function initialize(exchange, market, pProductsPanel, callBackFunction) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> Entering function."); } - - /* Remember the Products Panel */ - - productsPanel = pProductsPanel; - - /* Listen to the event of change of status */ - - productsPanel.container.eventHandler.listenToEvent("Product Card Status Changed", onProductCardStatusChanged); - - /* Legacy code to clean */ - - marketId = market; - exchangeId = exchange; - - chartGrid = newChartGrid(); - - recalculateScale(); - moveViewPortToCurrentDatetime(); - - /* Event Subscriptions - we need this events to be fired first here and then in active Plotters. */ - - viewPort.eventHandler.listenToEvent("Offset Changed", onOffsetChanged); - viewPort.eventHandler.listenToEvent("Zoom Changed", onZoomChanged); - canvas.eventHandler.listenToEvent("Drag Finished", onDragFinished); - - initializeCompetitionPlotters(onCompetitionPlottersInitialized); - - function onCompetitionPlottersInitialized(err) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onCompetitionPlottersInitialized -> Entering function."); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onCompetitionPlottersInitialized -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onCompetitionPlottersInitialized -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onFileCursorReady -> Received Unexpected Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - } - - initializeProductPlotters(onProductPlottersInitialized); - - function onProductPlottersInitialized(err) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> Entering function."); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> Received OK Response."); } - - initializationReady = true; - callBackFunction(GLOBAL.CUSTOM_OK_RESPONSE); - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> Received FAIL Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> Received Unexpected Response."); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onCompetitionPlottersInitialized -> onProductPlottersInitialized -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> onCompetitionPlottersInitialized -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initialize -> err = " + err); } - callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function initializeCompetitionPlotters(callBack) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> Entering function."); } - - /* At this current version of the platform, we will support only one competition with only one plotter. */ - - const COMPETITION_HOST = "AAArena"; - const COMPETITION = "First-Closed-Door"; - - let objName = COMPETITION_HOST + "-" + COMPETITION; - let storage = newCompetitionStorage(objName); - - let host = ecosystem.getHost(COMPETITION_HOST); - let competition = ecosystem.getCompetition(host, COMPETITION); - - storage.initialize(host, competition, DEFAULT_EXCHANGE, DEFAULT_MARKET, onCompetitionStorageInitialized); - - function onCompetitionStorageInitialized(err) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> Entering function."); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> Received FAIL Response."); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> Received Unexpected Response."); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - } - - /* Now we have all the initial data loaded and ready to be delivered to the new instance of the plotter. */ - - let plotter = getNewPlotter(competition.plotter.host, competition.plotter.codeName, competition.plotter.moduleName); - - plotter.container.displacement.parentDisplacement = thisObject.container.displacement; - plotter.container.frame.parentFrame = thisObject.container.frame; - - plotter.container.parentContainer = thisObject.container; - - plotter.container.frame.width = thisObject.container.frame.width * 1; - plotter.container.frame.height = thisObject.container.frame.height * 1; - - 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; - - /* We add the profile picture of each participant, because the plotter will need it. */ - - for (let k = 0; k < competition.participants.length; k++) { - - let participant = competition.participants[k]; - let devTeam = ecosystem.getTeam(participant.devTeam); - let bot = ecosystem.getBot(devTeam, participant.bot); - participant.profilePicture = bot.profilePicture; - - } - - plotter.initialize(competition, storage, datetime, timePeriod, onPlotterInizialized); - - function onPlotterInizialized(err) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> Entering function."); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> Received OK Response."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> Received FAIL Response."); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> Received Unexpected Response."); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - } - - let competitionPlotter = { - plotter: plotter, - storage: storage - }; - - /* Add the new Active Protter to the Array */ - - competitionPlotters.push(competitionPlotter); - - /* Create The Profie Picture FloatingObject */ - - if (competitionPlotter.plotter.payload !== undefined) { - - for (let k = 0; k < competition.participants.length; k++) { - - let participant = competition.participants[k]; - let devTeam = ecosystem.getTeam(participant.devTeam); - let bot = ecosystem.getBot(devTeam, participant.bot); - let imageId = participant.devTeam + "." + participant.profilePicture; - - competitionPlotter.plotter.payload[k].profile.downLabel = bot.displayName; - competitionPlotter.plotter.payload[k].profile.imageId = imageId; - - canvas.floatingSpace.profileBalls.createNewProfileBall(competitionPlotter.plotter.payload[k], onProfileBallCreated) - - function onProfileBallCreated(err, pProfileHandle) { - - competitionPlotter.plotter.payload[k].profile.handle = pProfileHandle; - - } - } - } - - callBack(GLOBAL.DEFAULT_OK_RESPONSE); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> onPlotterInizialized -> err = " + err); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initializeCompetitionPlotters -> onCompetitionStorageInitialized -> err = " + err); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initializeCompetitionPlotters -> err = " + err); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function initializeProductPlotters(callBack) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> Entering function."); } - - /* Lets get all the cards that needs to be loaded. */ - - let initializationCounter = 0; - let loadingProductCards = productsPanel.getLoadingProductCards(); - let okCounter = 0; - let failCounter = 0; - - for (let i = 0; i < loadingProductCards.length; i++) { - - /* For each one, we will initialize the associated plotter. */ - - initializeProductPlotter(loadingProductCards[i], onProductPlotterInitialized); - - function onProductPlotterInitialized(err) { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> Entering function."); } - - initializationCounter++; - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> Received OK Response."); } - okCounter++; - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> Received FAIL Response."); } - failCounter++; - break; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> Received Unexpected Response."); } - failCounter++; - break; - } - } - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> initializationCounter = " + initializationCounter); } - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> okCounter = " + okCounter); } - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> failCounter = " + failCounter); } - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> loadingProductCards.length = " + loadingProductCards.length); } - - if (initializationCounter === loadingProductCards.length) { // This was the last one. - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> onProductPlotterInitialized -> Last Product Loaded."); } - - /* If less than 50% of plotters are initialized then we return FAIL. */ - - if (okCounter >= 1) { - callBack(GLOBAL.DEFAULT_OK_RESPONSE); - } else { - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initializeProductPlotters -> err = " + err); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function initializeProductPlotter(pProductCard, callBack) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> Entering function."); } - - /* Before Initializing a Plotter, we need the Storage it will use, loaded with the files it will initially need. */ - - let objName = pProductCard.devTeam.codeName + "-" + pProductCard.bot.codeName + "-" + pProductCard.product.codeName; - let storage = newProductStorage(objName); - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> objName = " + objName); } - - /* - - Before Initializing the Storage, we will put the Product Card 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 < pProductCard.product.dataSets.length; i++) { - - let thisSet = pProductCard.product.dataSets[i]; - - switch (thisSet.type) { - case 'Market Files': { - storage.eventHandler.listenToEvent('Market File Loaded', pProductCard.onMarketFileLoaded); - } - break; - - case 'Daily Files': { - storage.eventHandler.listenToEvent('Daily File Loaded', pProductCard.onDailyFileLoaded); - } - break; - - case 'Single File': { - storage.eventHandler.listenToEvent('Single File Loaded', pProductCard.onSingleFileLoaded); - } - break; - - case 'File Sequence': { - storage.eventHandler.listenToEvent('File Sequence Loaded', pProductCard.onFileSequenceLoaded); - } - break; - } - } - - storage.initialize(pProductCard.devTeam, pProductCard.bot, pProductCard.product, DEFAULT_EXCHANGE, DEFAULT_MARKET, datetime, timePeriod, onProductStorageInitialized); - - function onProductStorageInitialized(err) { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> key = " + pProductCard.devTeam.codeName + "-" + pProductCard.bot.codeName + "-" + pProductCard.product.codeName); } - - switch (err.result) { - case GLOBAL.DEFAULT_OK_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> Received OK Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> The plotter will be started."); } - break; - } - - case GLOBAL.DEFAULT_FAIL_RESPONSE.result: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> Received FAIL Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> The plotter will not be started."); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - - default: { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> Received Unexpected Response."); } - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> The plotter will not be started."); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - return; - } - } - - /* Now we have all the initial data loaded and ready to be delivered to the new instance of the plotter. */ - - let plotter = getNewPlotter(pProductCard.product.plotter.devTeam, pProductCard.product.plotter.codeName, pProductCard.product.plotter.moduleName); - - plotter.container.displacement.parentDisplacement = thisObject.container.displacement; - plotter.container.frame.parentFrame = thisObject.container.frame; - - plotter.container.parentContainer = thisObject.container; - - plotter.container.frame.width = thisObject.container.frame.width * 1; - plotter.container.frame.height = thisObject.container.frame.height * 1; - - 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.initialize(storage, DEFAULT_EXCHANGE, DEFAULT_MARKET, datetime, timePeriod, onPlotterInizialized); - - function onPlotterInizialized() { - - try { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> onPlotterInizialized -> Entering function."); } - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotter -> onProductStorageInitialized -> onPlotterInizialized -> key = " + pProductCard.product.plotter.devTeam + "-" + pProductCard.product.plotter.codeName + "-" + pProductCard.product.plotter.moduleName); } - - try { - plotter.positionAtDatetime(INITIAL_DATE); - } catch (err) { - // If the plotter does not implement this function its ok. - } - - let productPlotter = { - productCard: pProductCard, - plotter: plotter, - storage: storage, - profile: undefined, - notes: undefined - }; - - /* Let the Plotter listen to the event of Cursor Files loaded, so that it can reack recalculating if needed. */ - - storage.eventHandler.listenToEvent('Daily File Loaded', plotter.onDailyFileLoaded); - - /* Lets load now this plotter panels. */ - - productPlotter.panels = []; - - for (let i = 0; i < pProductCard.product.plotter.module.panels.length; i++) { - - let panelConfig = pProductCard.product.plotter.module.panels[i]; - - let parameters = { - devTeam: pProductCard.product.plotter.devTeam, - plotterCodeName: pProductCard.product.plotter.codeName, - moduleCodeName: pProductCard.product.plotter.moduleName, - panelCodeName: panelConfig.codeName - } - - let plotterPanelHandle = canvas.panelsSpace.createNewPanel("Plotter Panel", parameters); - let plotterPanel = canvas.panelsSpace.getPanel(plotterPanelHandle); - - /* Connect Panel to the Plotter via an Event. */ - - if (panelConfig.event !== undefined) { - - productPlotter.plotter.container.eventHandler.listenToEvent(panelConfig.event, plotterPanel.onEventRaised); - - } - - productPlotter.panels.push(plotterPanelHandle); - } - - /* Create The Profie Picture FloatingObject */ - - if (productPlotter.plotter.payload !== undefined) { - - let imageId = pProductCard.bot.devTeam + "." + pProductCard.bot.profilePicture; - - productPlotter.plotter.payload.profile.upLabel = pProductCard.product.shortDisplayName; - productPlotter.plotter.payload.profile.downLabel = pProductCard.bot.displayName; - productPlotter.plotter.payload.profile.imageId = imageId; - - canvas.floatingSpace.profileBalls.createNewProfileBall(productPlotter.plotter.payload, onProfileBallCreated) - - function onProfileBallCreated(err, pProfileHandle) { - - productPlotter.profile = pProfileHandle; - - /* There is no policy yet of what to do if this fails. */ - } - - /* Create the Text Notes */ - - canvas.floatingSpace.noteSets.createNoteSet(productPlotter.plotter.payload, productPlotter.plotter.container.eventHandler, onNoteSetCreated); - - function onNoteSetCreated(err, pNoteSetHandle) { - - productPlotter.noteSet = pNoteSetHandle; - - /* There is no policy yet of what to do if this fails. */ - } - } - - /* Add the new Active Protter to the Array */ - - productPlotters.push(productPlotter); - - callBack(GLOBAL.DEFAULT_OK_RESPONSE); - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initializeProductPlotter -> onProductStorageInitialized -> onPlotterInizialized -> err = " + err); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initializeProductPlotter -> onProductStorageInitialized -> err = " + err); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - } catch (err) { - - if (ERROR_LOG === true) { logger.write("[ERROR] initializeProductPlotter -> err = " + err); } - callBack(GLOBAL.DEFAULT_FAIL_RESPONSE); - } - } - - function onProductCardStatusChanged(pProductCard) { - - if (INFO_LOG === true) { logger.write("[INFO] onProductCardStatusChanged -> Entering function."); } - - if (pProductCard.status === PRODUCT_CARD_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 < productPlotters.length; i++) { - - if (productPlotters[i].productCard.code === pProductCard.code) { - - found = true; - - } - - } - - if (found === false) { - - initializeProductPlotter(pProductCard, onProductPlotterInitialized); - - function onProductPlotterInitialized(err) { - - if (INFO_LOG === true) { logger.write("[INFO] initializeProductPlotters -> onProductPlotterInitialized -> Entering function."); } - - /* There is no policy yet of what to do if this fails. */ - - } - } - } - - if (pProductCard.status === PRODUCT_CARD_STATUS.OFF) { - - /* If the plotter of this card is on our Active Plotters list, then we remove it. */ - - for (let i = 0; i < productPlotters.length; i++) { - - if (productPlotters[i].productCard.code === pProductCard.code) { - - if (productPlotters[i].profile !== undefined) { - - canvas.floatingSpace.profileBalls.destroyProfileBall(productPlotters[i].profile); - - } - - /* Destroyd the Note Set */ - - canvas.floatingSpace.noteSets.destroyNoteSet(productPlotters[i].noteSet); - - /* Finally the panels. */ - - for (let j = 0; j < productPlotters[i].panels.length; j++) { - - canvas.panelsSpace.destroyPanel(productPlotters[i].panels[j]); - - } - - productPlotters.splice(i, 1); // Delete item from array. - - } - } - } - } - - function onZoomChanged(event) { - - if (INFO_LOG === true) { logger.write("[INFO] onZoomChanged -> Entering function."); } - - if (initializationReady === true) { - - level = event.newLevel; // for debugging purposes only - - let currentTimePeriod = timePeriod; - - timePeriod = recalculatePeriod(event.newLevel); - - /* If the period changes, we need to spread the word in cascade towards all the depending objects. */ - - if (timePeriod !== currentTimePeriod) { - - for (let i = 0; i < productPlotters.length; i++) { - - let productPlotter = productPlotters[i]; - - productPlotter.productCard.setTimePeriod(timePeriod); - productPlotter.storage.setTimePeriod(timePeriod); - productPlotter.plotter.setTimePeriod(timePeriod); - - } - - for (let i = 0; i < competitionPlotters.length; i++) { - - let competitionPlotter = competitionPlotters[i]; - - competitionPlotter.plotter.setTimePeriod(timePeriod); - - } - } - - recalculateCurrentDatetime(); - - } - } - - function onDragFinished() { - - if (INFO_LOG === true) { logger.write("[INFO] onDragFinished -> Entering function."); } - - if (initializationReady === true) { - - if (thisObject.container.frame.isInViewPort() && tooSmall() === false) { - - - - } - } - } - - function onOffsetChanged() { - - if (INFO_LOG === true) { logger.write("[INFO] onOffsetChanged -> Entering function."); } - - if (initializationReady === true) { - - if (thisObject.container.frame.isInViewPort()) { - - recalculateCurrentDatetime(); - - } - } - - } - - function recalculateCurrentDatetime() { - - if (INFO_LOG === true) { logger.write("[INFO] recalculateCurrentDatetime -> Entering function."); } - - /* - - 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 me - must calculate. - - */ - - let center = { - x: (viewPort.visibleArea.bottomRight.x - viewPort.visibleArea.bottomLeft.x) / 2, - y: (viewPort.visibleArea.bottomRight.y - viewPort.visibleArea.topRight.y) / 2 - }; - - center = unTransformThisPoint(center, thisObject.container); - center = timeLineCoordinateSystem.unInverseTransform(center, thisObject.container.frame.height); - - let newDate = new Date(0); - newDate.setUTCSeconds(center.x / 1000); - - datetime = newDate; - - for (let i = 0; i < productPlotters.length; i++) { - - let productPlotter = productPlotters[i]; - - productPlotter.productCard.setDatetime(datetime); - productPlotter.storage.setDatetime(datetime); - productPlotter.plotter.setDatetime(datetime); - - } - - for (let i = 0; i < competitionPlotters.length; i++) { - - let competitionPlotter = competitionPlotters[i]; - - competitionPlotter.plotter.setDatetime(datetime); - - } - - thisObject.container.eventHandler.raiseEvent("Datetime Changed", datetime); - - } - - function getContainer(point) { - - if (INFO_LOG === true) { logger.write("[INFO] getContainer -> Entering function."); } - - let container; - - /* First we check if this point is inside this space. */ - - if (this.container.frame.isThisPointHere(point) === true) { - - // return this.container; Currently there is a BUG in which if this is enabled the datetime of the control panel is not recalculated. - - } else { - - /* This point does not belong to this space. */ - - return undefined; - } - - } - - function setDatetime(pDatetime) { - - if (INFO_LOG === true) { logger.write("[INFO] setDatetime -> Entering function."); } - - /* This function is used when the time is changed through the user interface, but without zooming or panning. */ - /* No matter if the day changed or not, we need to inform all visible Plotters. */ - - if (thisObject.container.frame.isInViewPort()) { - - for (let i = 0; i < productPlotters.length; i++) { - - let productPlotter = productPlotters[i]; - - productPlotter.productCard.setDatetime(pDatetime); - productPlotter.storage.setDatetime(pDatetime); - productPlotter.plotter.setDatetime(pDatetime); - - /* The time has changed, but the viewPort is still on the same place, so we request any of the plotters to reposition it. */ - - if (productPlotter.plotter.positionAtDatetime !== undefined) { - productPlotter.plotter.positionAtDatetime(pDatetime); - } - - } - - for (let i = 0; i < competitionPlotters.length; i++) { - - let competitionPlotter = competitionPlotters[i]; - - competitionPlotter.plotter.setDatetime(datetime); - - } - } - } - - function recalculateScale() { - - if (INFO_LOG === true) { logger.write("[INFO] recalculateScale -> Entering function."); } - - let minValue = { - x: EARLIEST_DATE.valueOf(), - y: 0 - }; - - let maxValue = { - x: MAX_PLOTABLE_DATE.valueOf(), - y: nextPorwerOf10(USDT_BTC_HTH) - }; - - - timeLineCoordinateSystem.initialize( - minValue, - maxValue, - thisObject.container.frame.width, - thisObject.container.frame.height - ); - - } - - function tooTiny() { - - if (INTENSIVE_LOG === true) { logger.write("[INFO] tooTiny -> Entering function."); } - - if (viewPort.zoomLevel < Math.trunc(-28.25 * 100) / 100) { - return true; - } else { - return false; - } - - } - - function tooSmall() { - - if (INFO_LOG === true) { logger.write("[INFO] tooSmall -> Entering function."); } - - if (viewPort.zoomLevel < Math.trunc(-27.25 * 100) / 100) { - return true; - } else { - return false; - } - - } - - function draw() { - - if (INTENSIVE_LOG === true) { logger.write("[INFO] draw -> Entering function."); } - - if (productPlotters === undefined) { return; } // We need to wait - - if (thisObject.container.frame.isInViewPort()) { - - this.container.frame.draw(); - - drawBackground(); - - chartGrid.draw(thisObject.container, timeLineCoordinateSystem); - - /* First the Product Plotters. */ - - for (let i = 0; i < productPlotters.length; i++) { - - let productPlotter = productPlotters[i]; - productPlotter.plotter.draw(); - - } - - /* Then the competition plotters. */ - - for (let i = 0; i < competitionPlotters.length; i++) { - - let competitionPlotter = competitionPlotters[i]; - competitionPlotter.plotter.draw(); - } - } - } - - function drawBackground() { - - if (INTENSIVE_LOG === true) { logger.write("[INFO] drawBackground -> Entering function."); } - - let targetLabelFontSize = 150; - let fontSizeIncrement = 12.5; - let currentFontSize = 150; - - let market = markets.get(marketId); - let label = market.assetA + " " + market.assetB; - - //label = '' + level; // Math.trunc(timePeriod / 1000 / 60); - - if (tooTiny() === true ) { - return; - } - - let topPoint = { - x: 0, - y: 0 - }; - - let bottomPoint = { - x: 0, - y: thisObject.container.frame.height - }; - - topPoint = transformThisPoint(topPoint, thisObject.container); - bottomPoint = transformThisPoint(bottomPoint, thisObject.container); - - /* We want the label of the market to be always centered in the middle of the screen, unless the upper or lower border of the frame is visible on the screen */ - - let point; - - if (targetLabelFontSize !== currentFontSize) { - if (targetLabelFontSize > currentFontSize) { - currentFontSize = currentFontSize + fontSizeIncrement; - } else { - currentFontSize = currentFontSize - fontSizeIncrement; - } - } - - if (topPoint.y > viewPort.visibleArea.topLeft.y || bottomPoint.y < viewPort.visibleArea.bottomRight.y) { - - targetLabelFontSize = 150 / 2; - - point = { - x: 0, - y: thisObject.container.frame.height / 2 - }; - - point = transformThisPoint(point, thisObject.container); - - point = { - x: (viewPort.visibleArea.bottomRight.x - viewPort.visibleArea.bottomLeft.x) / 2 - label.length / 2 * currentFontSize * 0.60, - y: point.y + currentFontSize * 0.60 / 2 - }; - - } else { - - targetLabelFontSize = 150; - - point = { - x: (viewPort.visibleArea.bottomRight.x - viewPort.visibleArea.bottomLeft.x) / 2 - label.length / 2 * currentFontSize * 0.60, - y: (viewPort.visibleArea.bottomLeft.y - viewPort.visibleArea.topLeft.y) / 2 + currentFontSize * 0.60 / 2 - }; - - } - - browserCanvasContext.font = currentFontSize + 'px Courier New'; - browserCanvasContext.fillStyle = 'rgba(75, 86, 235, 0.07)'; - browserCanvasContext.fillText(label, point.x, point.y); - - } - - function moveViewPortToCurrentDatetime() { - - if (INFO_LOG === true) { logger.write("[INFO] moveViewPortToCurrentDatetime -> Entering function."); } - - let targetPoint = { - x: datetime.valueOf(), - y: 0 // we wont touch the y axis here. - }; - - /* Lets put this point in the coordinate system of the viewPort */ - - targetPoint = timeLineCoordinateSystem.transformThisPoint(targetPoint); - targetPoint = transformThisPoint(targetPoint, thisObject.container); - - /* Lets get the point on the viewPort coordinate system of the center of the visible screen */ - - let center = { - x: (viewPort.visibleArea.bottomRight.x - viewPort.visibleArea.bottomLeft.x) / 2, - y: (viewPort.visibleArea.bottomRight.y - viewPort.visibleArea.topRight.y) / 2 - }; - - /* Lets calculate the displace vector, from the point we want at the center, to the current center. */ - - let displaceVector = { - x: center.x - targetPoint.x, - y: 0 - }; - - viewPort.displace(displaceVector); - } -} - diff --git a/Utilities.js b/Utilities.js deleted file mode 100644 index 910e8ccc8..000000000 --- a/Utilities.js +++ /dev/null @@ -1,79 +0,0 @@ - - -function transformThisPoint(point, container) { - - /* We make the point relative to the current frame */ - - point = container.frame.frameThisPoint(point); - - /* We add the possible displacement */ - - point = container.displacement.displaceThisPoint(point); - - /* We viewport Transformation. */ - - point = viewPort.zoomThisPoint(point); - - return point; -} - -function unTransformThisPoint(point, container) { - - point = viewPort.unzoomThisPoint(point); - point = container.displacement.undisplaceThisPoint(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 getDateFromPoint(point, container, timeLineCoordinateSystem) { - - point = unTransformThisPoint(point, container); - point = timeLineCoordinateSystem.unInverseTransform(point, container.frame.height); - - let date = new Date(point.x); - - return date; -} - - -function getRateFromPoint(point, container, timeLineCoordinateSystem) { - - point = unTransformThisPoint(point, container); - point = timeLineCoordinateSystem.unInverseTransform(point, container.frame.height); - - return point.y; -} - - -function getMilisecondsFromPoint(point, container, timeLineCoordinateSystem) { - - point = unTransformThisPoint(point, container); - point = timeLineCoordinateSystem.unInverseTransform(point, container.frame.height); - - return point.x; -} - - -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; -} \ No newline at end of file diff --git a/ViewPort.js b/ViewPort.js deleted file mode 100644 index 6ca6a9d33..000000000 --- a/ViewPort.js +++ /dev/null @@ -1,415 +0,0 @@ - -function newViewPort() { - - const CONSOLE_LOG = true; - - let ANIMATION_INCREMENT = 0.25; - const TOP_MARGIN = 15; - const BOTTOM_MARGIN = 0; - const LEFT_MARGIN = 50; - const RIGHT_MARGIN = 50; - - let viewPort = { - visibleArea: undefined, - eventHandler: undefined, - zoomLevel: INITIAL_ZOOM_LEVEL, - mousePosition: undefined, - applyZoom: applyZoom, - zoomFontSize: zoomFontSize, - zoomThisPoint: zoomThisPoint, - unzoomThisPoint: unzoomThisPoint, - isThisPointVisible: isThisPointVisible, - fitIntoVisibleArea: fitIntoVisibleArea, - displace: displace, - displaceTarget: displaceTarget, - animate: animate, - draw: draw, - initialize: initialize - }; - - zoomTargetLevel = INITIAL_ZOOM_LEVEL; - - let increment = 0.035; - - var offset = { - x: 0, - y: 0 - }; - - var targetOffset = { - x: 0, - y: 0 - }; - - let offsetIncrement = { - x: 0, - y: 0 - }; - - viewPort.mousePosition = { - x: 0, - y: 0 - }; - - viewPort.eventHandler = newEventHandler(); - - return viewPort; - - function initialize() { - - viewPort.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} - }; - - } - - function animate() { - - if (viewPort.zoomLevel < zoomTargetLevel) { - if (zoomTargetLevel - viewPort.zoomLevel < ANIMATION_INCREMENT) { - ANIMATION_INCREMENT = Math.abs(zoomTargetLevel - viewPort.zoomLevel); - } - viewPort.zoomLevel = viewPort.zoomLevel + ANIMATION_INCREMENT; - changeZoom(viewPort.zoomLevel - ANIMATION_INCREMENT, viewPort.zoomLevel); - } - - if (viewPort.zoomLevel > zoomTargetLevel) { - if (viewPort.zoomLevel - zoomTargetLevel < ANIMATION_INCREMENT) { - ANIMATION_INCREMENT = Math.abs(zoomTargetLevel - viewPort.zoomLevel); - } - viewPort.zoomLevel = viewPort.zoomLevel - ANIMATION_INCREMENT; - changeZoom(viewPort.zoomLevel + ANIMATION_INCREMENT, viewPort.zoomLevel); - } - - /* - if (offsetIncrement.x !== 0) { - - if (Math.trunc(Math.abs(targetOffset.x - offset.x) * 1000) >= Math.trunc(Math.abs(offsetIncrement.x) * 1000)) { - offset.x = offset.x + offsetIncrement.x; - } else { - offsetIncrement.x = 0; - } - } -*/ - - if (offsetIncrement.y !== 0) { - - if (Math.trunc(Math.abs(targetOffset.y - offset.y) * 1000) >= Math.trunc(Math.abs(offsetIncrement.y) * 1000)) { - offset.y = offset.y + offsetIncrement.y; - - //console.log("offset.y changed to " + offset.y) - - } else { - offsetIncrement.y = 0; - } - } - - - } - - function displace(displaceVector) { - - offset.x = offset.x + displaceVector.x; - offset.y = offset.y + displaceVector.y; - - let event = { - newOffset: offset - }; - - viewPort.eventHandler.raiseEvent("Offset Changed", event); - - //console.log("displace produced new Offset x = " + offset.x + " y = " + offset.y); - - } - - function displaceTarget(displaceVector) { - - - targetOffset.x = targetOffset.x + displaceVector.x; - targetOffset.y = targetOffset.y + displaceVector.y; - - - offsetIncrement = { - x: (targetOffset.x - offset.x) / 10, - y: (targetOffset.y - offset.y) / 10 - }; - - //console.log("displaceTarget x = " + targetOffset.x + " y = " + targetOffset.y); - - } - - - function applyZoom(amount) { - - //console.log("applyZoom amount: " + amount); - - if (amount > 0) { - - - if (zoomTargetLevel > -5) { - amount = amount * 2; - } - - if (zoomTargetLevel > 10) { - amount = amount * 3; - } - - if (zoomTargetLevel > 15) { - amount = amount * 4; - } - } - - if (amount < 0) { - - if (zoomTargetLevel > -4) { - amount = amount * 2; - } - - if (zoomTargetLevel > 13) { - amount = amount * 3; - } - - if (zoomTargetLevel > 19) { - amount = amount * 4; - } - } - - if (zoomTargetLevel + amount > 1000) { - return false; - } - - if (zoomTargetLevel <= -27 && amount < 0) { - amount = amount / 4; - } - - if (zoomTargetLevel < -27 && amount > 0) { - amount = amount / 4; - } - - if (zoomTargetLevel + amount < -28.25) { - return false; - } - - zoomTargetLevel = zoomTargetLevel + amount; - - ANIMATION_INCREMENT = Math.abs(zoomTargetLevel - viewPort.zoomLevel) / 3; - - let event = { - newLevel: zoomTargetLevel, - newOffset: offset, - type: undefined - }; - - if (amount > 0) { - event.type = "Zoom In"; - } else { - event.type = "Zoom Out"; - } - - //console.log("Zoom Changed > " + event.newLevel); - - viewPort.eventHandler.raiseEvent("Zoom Changed", event); - return true; - } - - - function changeZoom(oldLevel, newLevel) { - - let mouseNoZoom = unzoomThisPoint(viewPort.mousePosition, oldLevel); - let newMouse = zoomThisPoint(mouseNoZoom, newLevel); - - offset.x = offset.x - newMouse.x + viewPort.mousePosition.x; - offset.y = offset.y - newMouse.y + viewPort.mousePosition.y; - - //console.log("changeZoom produced new Offset x = " + offset.x + " y = " + offset.y); - - targetOffset.x = offset.x - targetOffset.y = offset.y ; - - offsetIncrement = { - x: 0, - y: 0 - }; - - } - - - function zoomFontSize(baseSize, level) { - - let zoomFactor = increment; // + increment * viewPort.zoomLevel / 100; - - if (level === undefined) { - - baseSize = baseSize * (1 + zoomFactor * viewPort.zoomLevel); - - } else { - - baseSize = baseSize * (1 + zoomFactor * level); - - } - - return baseSize; - } - - function fitIntoVisibleArea(point) { - - - /* Here we check the boundaries of the resulting points, so they dont go out of the visible area. */ - - if (point.x > viewPort.visibleArea.bottomRight.x + 1) { - point.x = viewPort.visibleArea.bottomRight.x + 1; - } - - if (point.x < viewPort.visibleArea.topLeft.x - 1) { - point.x = viewPort.visibleArea.topLeft.x - 1; - } - - if (point.y > viewPort.visibleArea.bottomRight.y + 1) { - point.y = viewPort.visibleArea.bottomRight.y + 1; - } - - if (point.y < viewPort.visibleArea.topLeft.y - 1) { - point.y = viewPort.visibleArea.topLeft.y - 1; - } - - return point; - } - - - function zoomThisPoint(point, level) { - - let zoomFactor = increment; // + increment * viewPort.zoomLevel / 100; - - if (level === undefined) { - - point.x = point.x * (1 + zoomFactor * viewPort.zoomLevel) + offset.x; - point.y = point.y * (1 + zoomFactor * viewPort.zoomLevel) + offset.y; - - } else { - - point.x = point.x * (1 + zoomFactor * level) + offset.x; - point.y = point.y * (1 + zoomFactor * level) + offset.y; - - } - - return point; - } - - function unzoomThisPoint(pointWithZoom, level) { - - let pointWithoutZoom; - let zoomFactor = increment; // + increment * viewPort.zoomLevel / 100; - - if (level === undefined) { - - pointWithoutZoom = { - x: (pointWithZoom.x - offset.x) / (1 + zoomFactor * viewPort.zoomLevel), - y: (pointWithZoom.y - offset.y) / (1 + zoomFactor * viewPort.zoomLevel) - }; - - } else { - - pointWithoutZoom = { - x: (pointWithZoom.x - offset.x) / (1 + zoomFactor * level), - y: (pointWithZoom.y - offset.y) / (1 + zoomFactor * level) - }; - - } - return pointWithoutZoom; - - } - - - - - function isThisPointVisible(point) { - - if (visibleArea === undefined) { - - getVisibleArea(); - - } - - if (point.x < visibleArea.topLeft.x || point.x > visibleArea.bottomRight.x || point.y < visibleArea.topLeft.y || point.y > visibleArea.bottomRight.y) { - return false; - } else { - return true; - } - - } - - - function draw() { -/* - drawGrid(10); - drawGrid(1); - drawGrid(0.1); -*/ - } - - function drawGrid(step) { - - let squareWidth = (viewPort.visibleArea.bottomRight.x - viewPort.visibleArea.bottomLeft.x) / step; - squareWidth = squareWidth + squareWidth * increment * viewPort.zoomLevel; - - let startingX = offset.x - Math.trunc(offset.x / squareWidth) * squareWidth; - let startingY = offset.y - Math.trunc(offset.y / squareWidth) * squareWidth; - let lineWidth = 10 / step + 10 / step * increment * viewPort.zoomLevel; - - if (lineWidth < 0.5) { - return; - } - - if (lineWidth > 2) { - lineWidth = 2; - } - - browserCanvasContext.beginPath(); - - for (var i = startingX; i < viewPort.visibleArea.bottomRight.x; i = i + squareWidth) { - - for (var j = startingY; j < viewPort.visibleArea.bottomRight.y; j = j + squareWidth) { - - let point1 = { - x: viewPort.visibleArea.bottomLeft.x, - y: j - }; - - let point2 = { - x: viewPort.visibleArea.bottomRight.x, - y: j - }; - - browserCanvasContext.moveTo(point1.x, point1.y); - browserCanvasContext.lineTo(point2.x, point2.y); - } - - let point3 = { - x: i, - y: viewPort.visibleArea.topLeft.y - }; - - let point4 = { - x: i, - y: viewPort.visibleArea.bottomLeft.y - }; - - browserCanvasContext.moveTo(point3.x, point3.y); - browserCanvasContext.lineTo(point4.x, point4.y); - } - browserCanvasContext.closePath(); - browserCanvasContext.strokeStyle = 'rgba(150, 150, 150, 0.' + Math.trunc(lineWidth + 1) + ')'; - - browserCanvasContext.lineWidth = lineWidth; - - browserCanvasContext.stroke(); - } - -} - - - - diff --git a/VolumeBar.js b/VolumeBar.js deleted file mode 100644 index 5f865d7ab..000000000 --- a/VolumeBar.js +++ /dev/null @@ -1,12 +0,0 @@ -function newVolumeBar() { - - var volumeBar = { - amountBuy: 0, - amountSell: 0, - begin: undefined, - end: undefined - }; - - return volumeBar; - -} \ No newline at end of file diff --git a/bin/Microsoft.NodejsTools.WebRole.dll b/bin/Microsoft.NodejsTools.WebRole.dll deleted file mode 100644 index 03b93f4a1..000000000 Binary files a/bin/Microsoft.NodejsTools.WebRole.dll and /dev/null differ diff --git a/index.html b/index.html index bfe37ce1c..18fab8db6 100644 --- a/index.html +++ b/index.html @@ -4,77 +4,30 @@ Advanced Algos - + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +
+
\ No newline at end of file diff --git a/package-lock.json b/package-lock.json index b523a6286..5986335fb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,24 +9,212 @@ "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-14.0.9.tgz", "integrity": "sha512-irP9phKfTXEZIcW2R+VNCtGHZJrXMWmSYp6RRfFn4BtAqtDRXF5z9JxCEQlAhNBf6X1koNi5k49tIAAAEJNlVQ==", "requires": { - "before-after-hook": "1.1.0", - "debug": "3.1.0", - "is-array-buffer": "1.0.0", - "is-stream": "1.1.0", - "lodash": "4.17.5", - "url-template": "2.0.8" + "before-after-hook": "^1.1.0", + "debug": "^3.1.0", + "is-array-buffer": "^1.0.0", + "is-stream": "^1.1.0", + "lodash": "^4.17.4", + "url-template": "^2.0.8" } }, + "ajv": { + "version": "5.5.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-5.5.2.tgz", + "integrity": "sha1-c7Xuyj+rZT49P5Qis0GtQiBdyWU=", + "requires": { + "co": "^4.6.0", + "fast-deep-equal": "^1.0.0", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.3.0" + } + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, "atob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.1.tgz", "integrity": "sha1-ri1acpR38onWDdf5amMUoi3Wwio=" }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.8.0.tgz", + "integrity": "sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==" + }, + "azure-storage": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/azure-storage/-/azure-storage-2.8.2.tgz", + "integrity": "sha1-GctjKx5WMK6uVEUaZNrBYLFlyEg=", + "requires": { + "browserify-mime": "~1.2.9", + "extend": "~1.2.1", + "json-edm-parser": "0.1.2", + "md5.js": "1.3.4", + "readable-stream": "~2.0.0", + "request": "~2.83.0", + "underscore": "~1.8.3", + "uuid": "^3.0.0", + "validator": "~9.4.1", + "xml2js": "0.2.8", + "xmlbuilder": "0.4.3" + }, + "dependencies": { + "har-validator": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.0.3.tgz", + "integrity": "sha1-ukAsJmGU8VlW7xXg/PJCmT9qff0=", + "requires": { + "ajv": "^5.1.0", + "har-schema": "^2.0.0" + } + }, + "oauth-sign": { + "version": "0.8.2", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.8.2.tgz", + "integrity": "sha1-Rqarfwrq2N6unsBWV4C31O/rnUM=" + }, + "request": { + "version": "2.83.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.83.0.tgz", + "integrity": "sha512-lR3gD69osqm6EYLk9wB/G1W/laGWjzH90t1vEa2xuxHD5KUrSzp9pUSfTm+YC5Nxt2T8nMPEvKlhbQayU7bgFw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.6.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.5", + "extend": "~3.0.1", + "forever-agent": "~0.6.1", + "form-data": "~2.3.1", + "har-validator": "~5.0.3", + "hawk": "~6.0.2", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.17", + "oauth-sign": "~0.8.2", + "performance-now": "^2.1.0", + "qs": "~6.5.1", + "safe-buffer": "^5.1.1", + "stringstream": "~0.0.5", + "tough-cookie": "~2.3.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.1.0" + }, + "dependencies": { + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + } + } + }, + "tough-cookie": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.3.4.tgz", + "integrity": "sha512-TZ6TTfI5NtZnuyy/Kecv+CnoROnyXn2DN97LontgQpCwsX2XyLYCC0ENhYkehSOwAp8rTQKc/NUIF7BkQ5rKLA==", + "requires": { + "punycode": "^1.4.1" + } + } + } + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "optional": true, + "requires": { + "tweetnacl": "^0.14.3" + } + }, "before-after-hook": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-1.1.0.tgz", "integrity": "sha512-VOMDtYPwLbIncTxNoSzRyvaMxtXmLWLUqr8k5AfC1BzLk34HvBXaQX8snOwQZ4c0aX8aSERqtJSiI9/m2u5kuA==" }, + "boom": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/boom/-/boom-4.3.1.tgz", + "integrity": "sha1-T4owBctKfjiJ90kDD9JbluAdLjE=", + "requires": { + "hoek": "4.x.x" + } + }, + "browserify-mime": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/browserify-mime/-/browserify-mime-1.2.9.tgz", + "integrity": "sha1-rrGvKN5sDXpqLOQK22j/GEIq8x8=" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "co": { + "version": "4.6.0", + "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", + "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=" + }, + "combined-stream": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.7.tgz", + "integrity": "sha512-brWl9y6vOB1xYPZcpZde3N9zDByXTosAeMDo4p1wzo6UMOX4vumB+TP1RZ76sfE6Md68Q0NJSrE/gbezd4Ul+w==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cryptiles": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/cryptiles/-/cryptiles-3.1.2.tgz", + "integrity": "sha1-qJ+7Ig9c4l7FboxKqKT9e1sNKf4=", + "requires": { + "boom": "5.x.x" + }, + "dependencies": { + "boom": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/boom/-/boom-5.2.0.tgz", + "integrity": "sha512-Z5BTk6ZRe4tXXQlkqftmsAUANpXmuwlsF5Oov8ThoMbQRzdGTA1ngYRW160GexgOgjsFOKJz0LYhoNi+2AMBUw==", + "requires": { + "hoek": "4.x.x" + } + } + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, "debug": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/debug/-/debug-3.1.0.tgz", @@ -35,6 +223,160 @@ "ms": "2.0.0" } }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "optional": true, + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "encoding": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.12.tgz", + "integrity": "sha1-U4tm8+5izRq1HsMjgp0flIDHS+s=", + "requires": { + "iconv-lite": "~0.4.13" + } + }, + "extend": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/extend/-/extend-1.2.1.tgz", + "integrity": "sha1-oPX9bPyDpf5J72mNYOyKYk3UV2w=" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fast-deep-equal": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz", + "integrity": "sha1-wFNHeBfIa1HaqFPIHgWbcz0CNhQ=" + }, + "fast-json-stable-stringify": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz", + "integrity": "sha1-1RQsDK7msRifh9OnYREGT4bIu/I=" + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.2.tgz", + "integrity": "sha1-SXBJi+YEwgwAXU9cI67NIda0kJk=", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "1.0.6", + "mime-types": "^2.1.12" + }, + "dependencies": { + "combined-stream": { + "version": "1.0.6", + "resolved": "http://registry.npmjs.org/combined-stream/-/combined-stream-1.0.6.tgz", + "integrity": "sha1-cj599ugBrFYTETp+RFqbactjKBg=", + "requires": { + "delayed-stream": "~1.0.0" + } + } + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "graphql": { + "version": "14.0.2", + "resolved": "https://registry.npmjs.org/graphql/-/graphql-14.0.2.tgz", + "integrity": "sha512-gUC4YYsaiSJT1h40krG3J+USGlwhzNTXSb4IOZljn9ag5Tj+RkoXrWp+Kh7WyE3t1NCfab5kzCuxBIvOMERMXw==", + "requires": { + "iterall": "^1.2.2" + } + }, + "graphql-client": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/graphql-client/-/graphql-client-2.0.1.tgz", + "integrity": "sha512-XXdjUD3mwsBDcUB7g+iXQeRt+3gmIbvA/Yx1nE5aq2RQmaJwiH4hEoYw27AW8cDR90pCrPlHJAb1jJq3zUMbZA==", + "requires": { + "isomorphic-fetch": "^2.2.1" + } + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.0.tgz", + "integrity": "sha512-+qnmNjI4OfH2ipQ9VQOw23bBd/ibtfbVdK2fYbY4acTDqKTW/YDp9McimZdDbG8iV9fZizUqQMD5xvriB146TA==", + "requires": { + "ajv": "^5.3.0", + "har-schema": "^2.0.0" + } + }, + "hash-base": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.0.4.tgz", + "integrity": "sha1-X8hoaEfs1zSZQDMZprCj8/auSRg=", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "hawk": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/hawk/-/hawk-6.0.2.tgz", + "integrity": "sha512-miowhl2+U7Qle4vdLqDdPt9m09K6yZhkLDTWGoUiUzrQCn+mHHSmfJgAyGaLRZbPmTqfFFjRV1QWCW0VWUJBbQ==", + "requires": { + "boom": "4.x.x", + "cryptiles": "3.x.x", + "hoek": "4.x.x", + "sntp": "2.x.x" + } + }, + "hoek": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz", + "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + }, "is-array-buffer": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-1.0.0.tgz", @@ -45,20 +387,337 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isomorphic-fetch": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/isomorphic-fetch/-/isomorphic-fetch-2.2.1.tgz", + "integrity": "sha1-YRrhrPFPXoH3KVB0coGf6XM1WKk=", + "requires": { + "node-fetch": "^1.0.1", + "whatwg-fetch": ">=0.10.0" + } + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "iterall": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/iterall/-/iterall-1.2.2.tgz", + "integrity": "sha512-yynBb1g+RFUPY64fTrFv7nsjRrENBQJaX2UL+2Szc9REFrSNm1rpSXHGzhmAy7a9uv3vlvgBlXnf9RqmPH1/DA==" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=", + "optional": true + }, + "json-edm-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/json-edm-parser/-/json-edm-parser-0.1.2.tgz", + "integrity": "sha1-HmCw/vG8CvZ7wNFG393lSGzWFbQ=", + "requires": { + "jsonparse": "~1.2.0" + } + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.3.1.tgz", + "integrity": "sha1-NJptRMU6Ud6JtAgFxdXlm0F9M0A=" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "jsonparse": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-1.2.0.tgz", + "integrity": "sha1-XAxWhRBxYOcv50ib3eoLRMK8Z70=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, "lodash": { - "version": "4.17.5", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.5.tgz", - "integrity": "sha512-svL3uiZf1RwhH+cWrfZn3A4+U58wbP0tGVTLQPbjplZxZ8ROD9VLuNgsRniTlLe7OlSqR79RUehXgpBW/s0IQw==" + "version": "4.17.11", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.11.tgz", + "integrity": "sha512-cQKh8igo5QUhZ7lg38DYWAxMvjSAKG0A8wGSVimP07SIUEK2UO+arSRKbRZWtelMtN5V0Hkwh5ryOto/SshYIg==" + }, + "md5.js": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.4.tgz", + "integrity": "sha1-6b296UogpawYsENA/Fdk1bCdkB0=", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "mime-db": { + "version": "1.36.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.36.0.tgz", + "integrity": "sha512-L+xvyD9MkoYMXb1jAmzI/lWYAxAMCPvIBSWur0PZ5nOf5euahRLVqH//FKW9mWp2lkqUgYiXPgkzfMUFi4zVDw==" + }, + "mime-types": { + "version": "2.1.20", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.20.tgz", + "integrity": "sha512-HrkrPaP9vGuWbLK1B1FfgAkbqNjIuy4eHlIYnFi7kamZyLLrGlo2mpcx0bBmNpKqBtYtAfGbodDddIgddSJC2A==", + "requires": { + "mime-db": "~1.36.0" + } }, "ms": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" }, + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + }, + "nonce": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/nonce/-/nonce-1.0.4.tgz", + "integrity": "sha1-7nMCrejBvvR28wG4yR9cxRpIdhI=" + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "process-nextick-args": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-1.0.7.tgz", + "integrity": "sha1-FQ4gt1ZZCtP5EJPyWk8q2L/zC6M=" + }, + "psl": { + "version": "1.1.29", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.1.29.tgz", + "integrity": "sha512-AeUmQ0oLN02flVHXWh9sSJF7mcdFq0ppid/JkErufc3hGIV/AMa8Fo9VgDo/cT2jFdOWoFvHp90qqBH54W+gjQ==" + }, + "punycode": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-1.4.1.tgz", + "integrity": "sha1-wNWmOycYgArY4esPpSachN1BhF4=" + }, + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "readable-stream": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.0.6.tgz", + "integrity": "sha1-j5A0HmilPMySh4jaz80Rs265t44=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "~1.0.0", + "process-nextick-args": "~1.0.6", + "string_decoder": "~0.10.x", + "util-deprecate": "~1.0.1" + } + }, + "request": { + "version": "2.88.0", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.0.tgz", + "integrity": "sha512-NAqBSrijGLZdM0WZNsInLJpkJokL72XYjUpnB0iwsRgxh7dB6COrHnTBNwN0E+lHDAJzu7kLAkDeY08z2/A0hg==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.0", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.4.3", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha1-G0KmJmoh8HQh0bC1S33BZ7AcATs=" + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "sax": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/sax/-/sax-0.5.8.tgz", + "integrity": "sha1-1HLbIo6zMcJQaw6MFVJK25OdEsE=" + }, + "sntp": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sntp/-/sntp-2.1.0.tgz", + "integrity": "sha512-FL1b58BDrqS3A11lJ0zEdnJ3UOKqVxawAkF3k7F0CVN7VQ34aZrV+G8BZ1WC9ZL7NyrwsW0oviwsWDgRuVYtJg==", + "requires": { + "hoek": "4.x.x" + } + }, + "sshpk": { + "version": "1.14.2", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.14.2.tgz", + "integrity": "sha1-xvxhZIo9nE52T9P8306hBeSSupg=", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "stringstream": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.6.tgz", + "integrity": "sha512-87GEBAkegbBcweToUrdzf3eLhWNg06FJTebl4BVJz/JgWy8CvEr9dRtX5qWphiynMSQlxxi+QqN0z5T32SLlhA==" + }, + "tough-cookie": { + "version": "2.4.3", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.4.3.tgz", + "integrity": "sha512-Q5srk/4vDM54WJsJio3XNn6K2sCG+CQ8G5Wz6bZhRZoAe/+TxjWB/GlFAnYEbkYVlON9FMk/fE3h2RLpPXo4lQ==", + "requires": { + "psl": "^1.1.24", + "punycode": "^1.4.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=", + "optional": true + }, + "underscore": { + "version": "1.8.3", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.8.3.tgz", + "integrity": "sha1-Tz+1OxBuYJf8+ctBCfKl6b36UCI=" + }, "url-template": { "version": "2.0.8", "resolved": "https://registry.npmjs.org/url-template/-/url-template-2.0.8.tgz", "integrity": "sha1-/FZaPMy/93MMd19WQflVV5FDnyE=" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "uuid": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.2.1.tgz", + "integrity": "sha512-jZnMwlb9Iku/O3smGWvZhauCf6cvvpKi4BKRiliS3cxnI+Gz9j5MEpTz2UFuXiKPJocb7gnsLHwiS05ige5BEA==" + }, + "validator": { + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/validator/-/validator-9.4.1.tgz", + "integrity": "sha512-YV5KjzvRmSyJ1ee/Dm5UED0G+1L4GZnLN3w6/T+zZm8scVua4sOhYKWTUrKa0H/tMiJyO9QLHMPN+9mB/aMunA==" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "whatwg-fetch": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-3.0.0.tgz", + "integrity": "sha512-9GSJUgz1D4MfyKU7KRqwOjXCXTqWdFNvEr7eUBYchQiVc744mqK/MzXPNR2WsPkmkOa4ywfg8C2n8h+13Bey1Q==" + }, + "xml2js": { + "version": "0.2.8", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.2.8.tgz", + "integrity": "sha1-m4FpCTFjH/CdGVdUn69U9PmAs8I=", + "requires": { + "sax": "0.5.x" + } + }, + "xmlbuilder": { + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-0.4.3.tgz", + "integrity": "sha1-xGFLp04K0ZbmCcknLNnh3bKKilg=" } } } diff --git a/package.json b/package.json index 130f55601..031336605 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,13 @@ }, "dependencies": { "@octokit/rest": "^14.0.9", - "atob": "^2.1.1" + "atob": "^2.1.1", + "azure-storage": "^2.8.2", + "graphql": "^14.0.2", + "graphql-client": "^2.0.1", + "lodash": "^4.17.11", + "nonce": "^1.0.4", + "request": "^2.88.0", + "retry": "^0.12.0" } } diff --git a/server.js b/server.js index 2276372ec..f1dc52194 100644 --- a/server.js +++ b/server.js @@ -1,653 +1,219 @@ -const CONSOLE_LOG = true; -if (CONSOLE_LOG === true) { +/* These 2 are global variables, that is why they do not have a let or var */ - console.log("Node Server Starting. Be happy, everything is going to be allright. :-)"); +CONSOLE_LOG = true; +CONSOLE_ERROR_LOG = true; +LOG_FILE_CONTENT = false; -} - -// serverConfig.localMode // This forces the server to read files from the local drive instead of github and to use develop storage and not production. +if (CONSOLE_LOG === true) { console.log("[INFO] server -> Node Server Starting."); } let serverConfig; -let githubData = new Map; +global.DEFAULT_OK_RESPONSE = { + result: "Ok", + message: "Operation Succeeded" +}; + +global.DEFAULT_FAIL_RESPONSE = { + result: "Fail", + message: "Operation Failed" +}; + +global.DEFAULT_RETRY_RESPONSE = { + result: "Retry", + message: "Retry Later" +}; + +global.CUSTOM_OK_RESPONSE = { + result: "Ok, but check Message", + message: "Custom Message" +}; + +global.CUSTOM_FAIL_RESPONSE = { + result: "Fail Because", + message: "Custom Message" +}; + +let storageData = new Map; + +let HTMLCloudScripts; // This are the html script tags needed to download the cloud web scripts. +let botScripts; // This module is the one which grabs user bots scrips from the storage, and browserifys them. + let ecosystem; let ecosystemObject; //'use strict'; -var http = require('http'); -var port = process.env.PORT || 1337; +let http = require('http'); +let port = process.env.PORT || 1337; + +let isHttpServerStarted = false; + +const STORAGE = require('./Server/Storage'); +let storage = STORAGE.newStorage(); + +let sessionManager; // This module have all authenticated active sessions. +let storageAccessManager; // This module manages the SAS for user to access the cloud storage from the browser. initialize(); function initialize() { - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> initialize -> Entering function."); } - readServerConfig(); + /* Clear all cached information. */ - function readServerConfig() { + storageData = new Map; - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readServerConfig -> Entering function."); } + const CONFIG_READER = require('./Server/ConfigReader'); + let configReader = CONFIG_READER.newConfigReader(); - let fs = require('fs'); - try { - let fileName = './this.server.config.json'; - fs.readFile(fileName, onFileRead); + configReader.initialize(ecosystem, ecosystemObject, storageData, onInitialized); - function onFileRead(err, file) { + function onInitialized(err, pServerConfig) { - try { + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readServerConfig -> onFileRead -> Entering function."); } + console.log("[ERROR] server -> initialize -> onInitialized -> err.message = " + err.message); + console.log("[ERROR] server -> initialize -> onInitialized -> Terminating Execution. "); - let fileText; - - fileText = file.toString(); - fileText = fileText.trim(); // remove first byte with some encoding. - - serverConfig = JSON.parse(fileText); - - readEcosystemConfig(); - } - catch (err) { - console.log("[ERROR] initialize -> readServerConfig -> onFileRead -> File = " + fileName + " Error = " + err); - } - - } + return; } - catch (err) { - console.log("[ERROR] initialize -> readServerConfig -> Error = " + err); - } - } - function readEcosystemConfig() { + serverConfig = pServerConfig; - try { + configReader.loadConfigs(onConfigsLoaded); - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readEcosystemConfig -> Entering function."); } + function onConfigsLoaded(err, pEcosystem, pEcosystemObject) { - if (CONSOLE_LOG === true && serverConfig.localMode === true) { + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - console.log("Hey! if you expect this to run at the web server I have some bad news for you son, serverConfig.localMode is true, that means the server wont find your local files. "); + console.log("[ERROR] server -> initialize -> onInitialized -> onConfigsLoaded -> err.message = " + err.message); + console.log("[ERROR] server -> initialize -> onInitialized -> onConfigsLoaded -> Terminating Execution. "); + return; } - /* - - This configuration file is the backbone of the system. The first file we are going to get is a template where other configurations are - injected and the files ends up inflated with all these configs in one single JSON object that in turn is later injected into a - javascript module with an object that is going to instantiate it at run-time. - - */ + ecosystem = pEcosystem; + ecosystemObject = pEcosystemObject; - if (serverConfig.localMode === true) { + storage.initialize(storageData, serverConfig) - let fs = require('fs'); - try { - let fileName = '../AAPlatform/ecosystem.json'; - fs.readFile(fileName, onFileRead); + const CLOUD_SCRIPTS = require('./Server/CloudScripts'); + let cloudScripts = CLOUD_SCRIPTS.newCloudScripts(); - function onFileRead(err, file) { + cloudScripts.initialize(ecosystem, ecosystemObject, serverConfig, storageData, onInitialized); - try { + function onInitialized(err) { - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readEcosystemConfig -> onFileRead -> Entering function."); } + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - ecosystem = file.toString(); - ecosystem = ecosystem.trim(); // remove first byte with some encoding. + console.log("[ERROR] server -> initialize -> onInitialized -> onConfigsLoaded -> onInitialized -> err.message = " + err.message); + console.log("[ERROR] server -> initialize -> onInitialized -> onConfigsLoaded -> onInitialized -> Terminating Execution. "); - ecosystemObject = JSON.parse(ecosystem); - readCompetitionsConfig(); - } - catch (err) { - console.log("[ERROR] initialize -> readEcosystemConfig -> onFileRead -> File = " + fileName + " Error = " + err); - } - - } - } - catch (err) { - console.log("[ERROR] initialize -> readEcosystemConfig -> File = " + fileName + " Error = " + err); + return; } - } else { + cloudScripts.loadCloudScripts(onLoadCompeted); - getGithubData('AdvancedAlgos', 'AAPlatform', 'ecosystem.json', onDataArrived) + function onLoadCompeted(err, pHTMLCloudScripts) { - function onDataArrived(pData) { + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - try { + console.log("[ERROR] server -> initialize -> onInitialized -> onConfigsLoaded -> onInitialized -> onLoadCompeted -> err.message = " + err.message); + console.log("[ERROR] server -> initialize -> onInitialized -> onConfigsLoaded -> onInitialized -> onLoadCompeted -> Terminating Execution. "); - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readEcosystemConfig -> onDataArrived -> Entering function."); } - - ecosystem = pData.toString(); - ecosystem = ecosystem.trim(); // remove first byte with some encoding. - - ecosystemObject = JSON.parse(ecosystem); - readCompetitionsConfig(); + return; } - catch (err) { - console.log("[ERROR] initialize -> readEcosystemConfig -> onDataArrived -> Error = " + err); - } - } - } - } - catch (err) { - console.log("[ERROR] initialize -> readEcosystemConfig -> Error = " + err); - } - } - function readCompetitionsConfig() { + const SESSION_MANAGER = require('./Server/SessionManager'); + sessionManager = SESSION_MANAGER.newSessionManager(); - try { + sessionManager.initialize(serverConfig, onInitialized); - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> Entering function."); } + function onInitialized() { - /* - - The file previously read contains the reference to all competiotion hosts and their competitions. We use these references to load the specific - configurations and inject them to the first file. - - Lets remember that these files can come from 3 different sources, and this patter is later repeated many times down this module: - - 1.If we are in debug mode, from the filesystem. - 2.If we are in production from a cache we keep in memory at the node server. - 3.If the cache does not have the file, then we get it from github.com, put in on the cache and serve it. - - 2 and 3 are done by the getGithubData function. - - */ + const STORAGE_ACCESS_MANAGER = require('./Server/StorageAccessManager'); + storageAccessManager = STORAGE_ACCESS_MANAGER.newStorageAccessManager(); - let requestsSent = 0; - let responsesReceived = 0; + storageAccessManager.initialize(onInitialized); - for (let i = 0; i < ecosystemObject.hosts.length; i++) { + function onInitialized() { - let host = ecosystemObject.hosts[i]; + const BOT_SCRIPTS = require('./Server/BotsScripts'); + botScripts = BOT_SCRIPTS.newBotScripts(); - /* In this first section we will load the competitions configurations. */ + botScripts.initialize(serverConfig, onInitialized); - for (let j = 0; j < host.competitions.length; j++) { + function onInitialized() { - let competition = host.competitions[j]; + HTMLCloudScripts = pHTMLCloudScripts; + startHtttpServer(); - requestsSent++; - - if (serverConfig.localMode === true) { - - let fs = require('fs'); - try { - let fileName = '../Competitions/' + host.codeName + '/' + competition.repo + '/' + competition.configFile; - fs.readFile(fileName, onFileRead); - - function onFileRead(err, pData) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onFileRead -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onFileRead -> fileName = " + fileName); } - - responsesReceived++; - - pData = pData.toString(); - pData = pData.trim(); // remove first byte with some encoding. - - let configObj = JSON.parse(pData); - - /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ - - configObj.repo = competition.repo; - configObj.configFile = competition.configFile; - - host.competitions[j] = configObj; - - if (requestsSent === responsesReceived) { - - readBotsAndPlottersConfig(); - - } - } - catch (err) { - console.log("[INFO] initialize -> readCompetitionsConfig -> onFileRead -> File = " + fileName + " Error = " + err); - } - } - } - catch (err) { - console.log("[ERROR] initialize -> readCompetitionsConfig -> File = " + fileName + " Error = " + err); - } - - } else { - - getGithubData(host.codeName, competition.repo, competition.configFile, onDataArrived) - - function onDataArrived(pData) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onDataArrived -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onDataArrived -> host.codeName = " + host.codeName); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onDataArrived -> competition.repo = " + competition.repo); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onDataArrived -> pData = " + pData); } - - responsesReceived++; - - pData = pData.toString(); - pData = pData.trim(); // remove first byte with some encoding. - - let configObj = JSON.parse(pData); - - /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ - - configObj.repo = competition.repo; - configObj.configFile = competition.configFile; - - host.competitions[j] = configObj; - - if (requestsSent === responsesReceived) { - - readBotsAndPlottersConfig(); - - } - - } - catch (err) { - console.log("[ERROR] initialize -> readCompetitionsConfig -> onDataArrived -> Error = " + err); - } - } - } - } - - /* In this second section we will load the plotters configurations. */ - - for (let j = 0; j < host.plotters.length; j++) { - - let plotter = host.plotters[j]; - - requestsSent++; - - if (serverConfig.localMode === true) { - - let fs = require('fs'); - try { - let fileName = '../Plotters/' + host.codeName + '/' + plotter.repo + '/' + plotter.configFile; - fs.readFile(fileName, onFileRead); - - function onFileRead(err, pData) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onFileRead -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onFileRead -> fileName = " + fileName); } - - responsesReceived++; - - pData = pData.toString(); - pData = pData.trim(); // remove first byte with some encoding. - - let configObj = JSON.parse(pData); - - /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ - - configObj.repo = plotter.repo; - configObj.configFile = plotter.configFile; - - host.plotters[j] = configObj; - - if (requestsSent === responsesReceived) { - - readBotsAndPlottersConfig(); - - } - } - catch (err) { - console.log("[ERROR] initialize -> readCompetitionsConfig -> onFileRead -> File = " + fileName + " Error = " + err); - } - - } - } - catch (err) { - console.log("[ERROR] initialize -> readCompetitionsConfig -> File = " + fileName + " Error = " + err); - } - - } else { - - getGithubData(host.codeName, plotter.repo, plotter.configFile, onDataArrived) - - function onDataArrived(pData) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onDataArrived -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onDataArrived -> host.codeName = " + host.codeName); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onDataArrived -> plotter.repo = " + plotter.repo); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readCompetitionsConfig -> onDataArrived -> pData = " + pData); } - - responsesReceived++; - - pData = pData.toString(); - pData = pData.trim(); // remove first byte with some encoding. - - let configObj = JSON.parse(pData); - - /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ - - configObj.repo = plotter.repo; - configObj.configFile = plotter.configFile; - - host.plotters[j] = configObj; - - if (requestsSent === responsesReceived) { - - readBotsAndPlottersConfig(); - - } - - } - catch (err) { - console.log("[ERROR] initialize -> readCompetitionsConfig -> onDataArrived -> Error = " + err); } } } } } } - catch (err) { - console.log("[ERROR] initialize -> readCompetitionsConfig -> Error = " + err); - } - } - - function readBotsAndPlottersConfig() { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> Entering function."); } - - /* - - Each bot has its configuration at its own repo since each team must be able to change it at will. - So what we do here is to use the master config at the AAPlatform repo that we already have on - memory and inject into it the config of each bot. - - Inmediatelly after that, we also load the Plotters configs using the same technique. - - */ - - let requestsSent = 0; - let responsesReceived = 0; - - for (let i = 0; i < ecosystemObject.devTeams.length; i++) { - - let devTeam = ecosystemObject.devTeams[i]; - - /* In the next section we are loading the bots configurations. */ - - for (let j = 0; j < devTeam.bots.length; j++) { - - let bot = devTeam.bots[j]; - - requestsSent++; - - if (serverConfig.localMode === true) { - - let fs = require('fs'); - try { - let fileName = '../Bots/' + devTeam.codeName + '/' + bot.repo + '/' + bot.configFile; - fs.readFile(fileName, onFileRead); - - function onFileRead(err, pData) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onFileRead -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onFileRead -> fileName = " + fileName); } - - responsesReceived++; - - pData = pData.toString(); - pData = pData.trim(); // remove first byte with some encoding. - - let configObj = JSON.parse(pData); - - /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ - - configObj.repo = bot.repo; - configObj.configFile = bot.configFile; - - addStoragePermissions(configObj); - - devTeam.bots[j] = configObj; - - if (requestsSent === responsesReceived) { - - startHtttpServer(); - - } - } - catch (err) { - console.log("[ERROR] initialize -> readBotsAndPlottersConfig -> onFileRead -> File = " + fileName + " Error = " + err); - } - - } - } - catch (err) { - console.log("[ERROR] initialize -> readBotsAndPlottersConfig -> File = " + fileName + " Error = " + err); - } - - } else { - - getGithubData(devTeam.codeName, bot.repo, bot.configFile, onDataArrived) - - function onDataArrived(pData) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onDataArrived -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onDataArrived -> devTeam.codeName = " + devTeam.codeName); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onDataArrived -> bot.repo = " + bot.repo); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onDataArrived -> pData = " + pData); } - - responsesReceived++; - - pData = pData.toString(); - pData = pData.trim(); // remove first byte with some encoding. - - let configObj = JSON.parse(pData); - - /* Since we are going to replace the full bot object and we dont want to lose these two properties, we do this: */ - - configObj.repo = bot.repo; - configObj.configFile = bot.configFile; - - addStoragePermissions(configObj); - - devTeam.bots[j] = configObj; - - if (requestsSent === responsesReceived) { - - startHtttpServer(); - - } - - } - catch (err) { - console.log("[ERROR] initialize -> readBotsAndPlottersConfig -> onDataArrived -> Error = " + err); - } - } - } - } - - /* In the next section we are loading the plotters configurations. */ - - for (let j = 0; j < devTeam.plotters.length; j++) { - - let plotter = devTeam.plotters[j]; - - requestsSent++; - - if (serverConfig.localMode === true) { - - let fs = require('fs'); - try { - let fileName = '../Plotters/' + devTeam.codeName + '/' + plotter.repo + '/' + plotter.configFile; - fs.readFile(fileName, onFileRead); - - function onFileRead(err, pData) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onFileRead -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onFileRead -> fileName = " + fileName); } - - responsesReceived++; - - pData = pData.toString(); - pData = pData.trim(); // remove first byte with some encoding. - - let configObj = JSON.parse(pData); - - /* Since we are going to replace the full plotter object and we dont want to lose these two properties, we do this: */ - - configObj.repo = plotter.repo; - configObj.configFile = plotter.configFile; - - devTeam.plotters[j] = configObj; - - if (requestsSent === responsesReceived) { - - startHtttpServer(); - - } - } - catch (err) { - console.log("[ERROR] initialize -> readPlottersConfig -> onFileRead -> File = " + fileName + " Error = " + err); - } - } - } - catch (err) { - console.log("[ERROR] initialize -> readPlottersConfig -> File = " + fileName + " Error = " + err); - } - - } else { - - getGithubData(devTeam.codeName, plotter.repo, plotter.configFile, onDataArrived) - - function onDataArrived(pData) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onDataArrived -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onDataArrived -> devTeam.codeName = " + devTeam.codeName); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onDataArrived -> plotter.repo = " + plotter.repo); } - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> onDataArrived -> pData = " + pData); } - - responsesReceived++; - - pData = pData.toString(); - pData = pData.trim(); // remove first byte with some encoding. - - let configObj = JSON.parse(pData); - - /* Since we are going to replace the full plotter object and we dont want to lose these two properties, we do this: */ - - configObj.repo = plotter.repo; - configObj.configFile = plotter.configFile; - - devTeam.plotters[j] = configObj; - - if (requestsSent === responsesReceived) { - - startHtttpServer(); - - } - - } - catch (err) { - console.log("[ERROR] initialize -> readBotsAndPlottersConfig -> onDataArrived -> Error = " + err); - } - } - } - } - } - - function addStoragePermissions(pConfigObj) { - - try { - - if (CONSOLE_LOG === true) { console.log("[INFO] initialize -> readBotsAndPlottersConfig -> addStoragePermissions -> Entering function."); } - - if (pConfigObj.storage !== undefined) { return; } // If this information is at the config file, then we take it, otherwise, we define it here. - - let fileUri; - let sas; - - switch (serverConfig.ambient) { - - case "Develop": { - - fileUri = "https://aadevelop.blob.core.windows.net"; - sas = "?sv=2017-07-29&ss=b&srt=sco&sp=rl&se=2019-01-01T17:39:27Z&st=2018-05-01T08:39:27Z&spr=https,http&sig=9atUljJam0E8zMg1VWQ0bGj2FqGYwtPIbImy5xyBMhE%3D" - - break; - } - - case "Production": { - - fileUri = "https://aaproduction.blob.core.windows.net"; - sas = "?sv=2017-07-29&ss=b&srt=sco&sp=rl&se=2019-01-01T03:17:16Z&st=2018-05-01T18:17:16Z&spr=https,http&sig=Ok4l7YxZlduPJrX31Y%2FHyajzxCHRnfEnTkoiRsYDXhk%3D" - - break; - } - } - - pConfigObj.storage = { - sas: sas, - fileUri: fileUri - }; - - } - catch (err) { - console.log("[ERROR] initialize -> readBotsAndPlottersConfig -> addStoragePermissions -> Error = " + err); - } - } - - } - catch (err) { - console.log("[ERROR] initialize -> readBotsAndPlottersConfig -> Error = " + err); - } } } function startHtttpServer() { - if (CONSOLE_LOG === true) { console.log("[INFO] startHtttpServer -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> startHtttpServer -> Entering function."); } try { - gWebServer = http.createServer(onBrowserRequest).listen(port); + if (isHttpServerStarted === false) { + + gWebServer = http.createServer(onBrowserRequest).listen(port); + isHttpServerStarted = true; + } } catch (err) { - console.log("[ERROR] startHtttpServer -> Error = " + err); + console.log("[ERROR] server -> startHtttpServer -> Error = " + err); } } function onBrowserRequest(request, response) { - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> request.url = " + request.url); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> Entering function."); } + if (CONSOLE_LOG === true && request.url.indexOf("NO-LOG") === -1) { console.log("[INFO] server -> onBrowserRequest -> request.url = " + request.url); } + + let htmlResponse; + let requestParameters = request.url.split("/"); + + + if (requestParameters[1].indexOf("index.html") >= 0) { + + /* + We use this to solve the problem when someone is arriving to the site with a sessionToken in the queryString. We extract here that + token, that will be sent later embedded into the HTML code, so that it can enter into the stardard circuit where any site can put + the sessionToken into their HTML code and from there the Browser app will log the user in. + */ + + let queryString = requestParameters[1].split("?"); + + requestParameters[1] = ""; + requestParameters[2] = queryString[1]; + homePage(); + + return; + } + - var htmlResponse; - var requestParameters = request.url.split("/"); switch (requestParameters[1]) { - case "clear-cache": { + case serverConfig.reinitializeCommand: { - githubData = new Map; + initialize(); - respondWithContent("command acepted", response); + respondWithContent("Node JS Server Reinitilized.", response); } break; + case "Plotter.js": { /* @@ -665,7 +231,7 @@ function onBrowserRequest(request, response) { function onFileRead(err, file) { - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> Entering function."); } try { @@ -678,7 +244,7 @@ function onBrowserRequest(request, response) { ' {' + '\n' + ' plotter = newPlotterName();' + '\n' + ' }' + '\n' + - ' break;' + '\n' + '\n' + ' break;' + '\n' + '\n' let devTeams = ecosystemObject.devTeams; let hosts = ecosystemObject.hosts; @@ -688,7 +254,7 @@ function onBrowserRequest(request, response) { function addToFileContent(pDevTeamsOrHosts) { - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> addToFileContent -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> addToFileContent -> Entering function."); } for (let i = 0; i < pDevTeamsOrHosts.length; i++) { @@ -724,7 +290,7 @@ function onBrowserRequest(request, response) { } catch (err) { - console.log("File Not Found: " + fileName + " or Error = " + err); + console.log("[ERROR] server -> onBrowserRequest -> File Not Found: " + fileName + " or Error = " + err); } } @@ -753,7 +319,7 @@ function onBrowserRequest(request, response) { function onFileRead(err, file) { - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> Entering function."); } try { @@ -776,7 +342,7 @@ function onBrowserRequest(request, response) { function addToFileContent(pDevTeamsOrHosts) { - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> addToFileContent -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> addToFileContent -> Entering function."); } for (let i = 0; i < pDevTeamsOrHosts.length; i++) { @@ -817,7 +383,7 @@ function onBrowserRequest(request, response) { } catch (err) { - console.log("File Not Found: " + fileName + " or Error = " + err); + console.log("[ERROR] server -> onBrowserRequest -> File Not Found: " + fileName + " or Error = " + err); } } @@ -834,7 +400,7 @@ function onBrowserRequest(request, response) { /* At this page we need to insert the configuration file for the whole system that we assamble before at the begining of this module - execution. So what we do is to load a template file with an insertion point where the configuration json is injected in. + execution. So what we do is to load a template file with an insertion point where the configuration json is injected in. */ @@ -845,22 +411,21 @@ function onBrowserRequest(request, response) { function onFileRead(err, file) { - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> Entering function."); } try { let fileContent = file.toString(); - let insertContent = JSON.stringify(ecosystemObject); + let insertContent = JSON.stringify(ecosystemObject); - fileContent = fileContent.replace('"@ecosystem.json@"', insertContent); + fileContent = fileContent.replace('"@ecosystem.json@"', insertContent); respondWithContent(fileContent, response); } catch (err) { - console.log("File Not Found: " + fileName); + console.log("[ERROR] server -> onBrowserRequest -> File Not Found: " + fileName + " or Error = " + err); } - } } catch (err) { @@ -870,54 +435,619 @@ function onBrowserRequest(request, response) { } break; + case "CloudVM": // This means the CloudVM folder. + { + let filePath = requestParameters[2]; + + if (requestParameters[3] !== undefined) { + filePath = filePath + "/" + requestParameters[3]; + } + + if (requestParameters[4] !== undefined) { + filePath = filePath + "/" + requestParameters[4]; + } + + respondWithFile(serverConfig.pathToCanvasApp +'/CloudVM/' + filePath, response); + + } + break; + + case "Exchange": // This means the Exchange folder. + { + let filePath = requestParameters[2]; + + if (requestParameters[3] !== undefined) { + filePath = filePath + "/" + requestParameters[3]; + } + + if (requestParameters[4] !== undefined) { + filePath = filePath + "/" + requestParameters[4]; + } + + respondWithFile(serverConfig.pathToCanvasApp + '/Exchange/' + filePath, response); + + } + break; + + case "Images": // This means the Scripts folder. + { + + let path = './Images/' + requestParameters[2]; + + if (requestParameters[3] !== undefined) { + + path = path + "/" + requestParameters[3]; + + } + + if (requestParameters[4] !== undefined) { + + path = path + "/" + requestParameters[4]; + + } + + if (requestParameters[5] !== undefined) { + + path = path + "/" + requestParameters[5]; + + } + + respondWithImage(path, response); + + } + break; + + case "favicon.ico": // This means the Scripts folder. + { + + respondWithImage('./Images/' + 'favicon.ico', response); + + } + break; + + case "BottomSpace": // This means the BottomSpace folder. + { + + respondWithFile(serverConfig.pathToCanvasApp + '/BottomSpace/' + requestParameters[2], response); + + } + break; + + case "TopSpace": // This means the TopSpace folder. + { + + respondWithFile(serverConfig.pathToCanvasApp + '/TopSpace/' + requestParameters[2], response); + + } + break; + + case "AABrowserAPI": // This means the Scripts folder. + { + switch (requestParameters[2]) { + + case "saveBotCode": { + + const AABROWSER_API_BOT_CODE = require('./AABrowserAPI/' + 'BotCode'); + let botCode = AABROWSER_API_BOT_CODE.newBotCode(); + + botCode.initialize(serverConfig); + + processPost(request, response, onPostReceived) + + function onPostReceived(pData) { + + let devTeam = requestParameters[3]; + let source = requestParameters[4] + "/" + requestParameters[5]; + let repo = requestParameters[6]; + let path; + + if (requestParameters[8] !== undefined) { + path = requestParameters[7] + "/" + requestParameters[8]; + } else { + path = requestParameters[7]; + } + botCode.saveBotCode(devTeam, source, repo, path, pData, onResponse); + + function onResponse(err) { + + respondWithContent(JSON.stringify(err), response); + } + } + + break; + } + + case "authenticateUser": { + + const AABROWSER_API_USER_AUTHENTICATION = require('./AABrowserAPI/' + 'UserAuthentication'); + let userAuthentication = AABROWSER_API_USER_AUTHENTICATION.newUserAuthentication(); + + userAuthentication.initialize(sessionManager, storageAccessManager); + userAuthentication.authenticateUser(requestParameters[3], onFinish); + + function onFinish(err, pUserProfile) { + + let responseToBrowser = { + err: err, + userProfile: pUserProfile + }; + + respondWithContent(JSON.stringify(responseToBrowser), response); + } + + break; + } + + case "newTeam": { + + const AABROWSER_API_TEAM_SETUP = require('./AABrowserAPI/' + 'TeamSetup'); + let teamSetup = AABROWSER_API_TEAM_SETUP.newTeamSetup(); + + teamSetup.initialize(serverConfig); + + let devTeamCodeName = decodeURI(requestParameters[3]); + let devTeamDisplayName = decodeURI(requestParameters[4]); + let userName = decodeURI(requestParameters[5]); + let botCodeName = decodeURI(requestParameters[6]); + let botDisplayName = decodeURI(requestParameters[7]); + let userId = decodeURI(requestParameters[8]); + let sharedSecret = decodeURI(requestParameters[9]); + + if (sharedSecret !== serverConfig.newTeamSharedSecret) { + + let customFailResponse = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: "The shared secret provided is incorrect" + } + + respondWithContent(JSON.stringify(customFailResponse), response); + + if (CONSOLE_LOG === true) { console.log("[WARN] server -> AABrowserAPI -> newTeam -> Shared Secret Received Incorrect."); } + + return; + + } + + teamSetup.newTeam(devTeamCodeName, devTeamDisplayName, userName, botCodeName, botDisplayName, userId, onSetupFinished); + + function onSetupFinished(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + /* We must re-load all the webserver caches.*/ + + initialize(); + + } + + respondWithContent(JSON.stringify(err), response); + } + break; + } + + case "deleteTeam": { + + const AABROWSER_API_TEAM_SETUP = require('./AABrowserAPI/' + 'TeamSetup'); + let teamSetup = AABROWSER_API_TEAM_SETUP.newTeamSetup(); + + teamSetup.initialize(serverConfig); + + let devTeamCodeName = decodeURI(requestParameters[3]); + let userName = decodeURI(requestParameters[4]); + let botCodeName = decodeURI(requestParameters[5]); + let userId = decodeURI(requestParameters[6]); + let sharedSecret = decodeURI(requestParameters[7]); + + if (sharedSecret !== serverConfig.deleteTeamSharedSecret) { + + let customFailResponse = { + result: global.DEFAULT_FAIL_RESPONSE.result, + message: "The shared secret provided is incorrect" + } + + respondWithContent(JSON.stringify(customFailResponse), response); + + if (CONSOLE_LOG === true) { console.log("[WARN] server -> AABrowserAPI -> deleteTeam -> Shared Secret Received Incorrect."); } + + return; + + } + + teamSetup.deleteTeam(devTeamCodeName, userName, botCodeName, userId, onSetupFinished); + + function onSetupFinished(err) { + + if (err.result === global.DEFAULT_OK_RESPONSE.result) { + + /* We must re-load all the webserver caches.*/ + + initialize(); + + } + + respondWithContent(JSON.stringify(err), response); + } + break; + } + } + } + break; + + case "Scripts": // This means the Scripts folder. + { + + if (requestParameters[2] === "AppLoader.js") { + + let fs = require('fs'); + try { + let fileName = './Scripts/' + 'AppLoader.js'; + fs.readFile(fileName, onFileRead); + + function onFileRead(err, file) { + + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> Entering function."); } + + try { + + let fileContent = file.toString(); + + addCloudWebScripts(); + addPlotters(); + + function addCloudWebScripts() { + + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> addCloudWebScripts -> Entering function."); } + + let firstPart = fileContent.substring(0, fileContent.indexOf('/* CloudWebScripts */') + 21); + let secondPart = fileContent.substring(fileContent.indexOf('/* CloudWebScripts */') + 21); + + fileContent = firstPart + HTMLCloudScripts + secondPart; + } + + function addPlotters() { + + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> addPlotters -> Entering function."); } + + let htmlLinePlotter = '' + '\n' + + ' "Plotters/@devTeam@/@repo@/@module@.js",' + + let htmlLinePlotterPanel = '' + '\n' + + ' "PlotterPanels/@devTeam@/@repo@/@module@.js",' + + let devTeams = ecosystemObject.devTeams; + let hosts = ecosystemObject.hosts; + + addScript(devTeams); + addScript(hosts); + + function addScript(pDevTeamsOrHosts) { + + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> addPlotters -> addScript -> Entering function."); } + + for (let i = 0; i < pDevTeamsOrHosts.length; i++) { + + let devTeam = pDevTeamsOrHosts[i]; + + for (let j = 0; j < devTeam.plotters.length; j++) { + + let plotter = devTeam.plotters[j]; + + if (plotter.modules !== undefined) { + + for (let k = 0; k < plotter.modules.length; k++) { + + let module = plotter.modules[k]; + + let htmlLineCopy = htmlLinePlotter; + + let stringToInsert; + stringToInsert = htmlLineCopy.replace('@devTeam@', devTeam.codeName); + stringToInsert = stringToInsert.replace('@repo@', plotter.repo); + stringToInsert = stringToInsert.replace('@module@', module.moduleName); + + let firstPart = fileContent.substring(0, fileContent.indexOf('/* Plotters */') + 14); + let secondPart = fileContent.substring(fileContent.indexOf('/* Plotters */') + 14); + + fileContent = firstPart + stringToInsert + secondPart; + + if (module.panels !== undefined) { + + for (let l = 0; l < module.panels.length; l++) { + + let panel = module.panels[l]; + + let htmlLineCopy = htmlLinePlotterPanel; + + let stringToInsert; + stringToInsert = htmlLineCopy.replace('@devTeam@', devTeam.codeName); + stringToInsert = stringToInsert.replace('@repo@', plotter.repo); + stringToInsert = stringToInsert.replace('@module@', panel.moduleName); + + let firstPart = fileContent.substring(0, fileContent.indexOf('/* PlotterPanels */') + 19); + let secondPart = fileContent.substring(fileContent.indexOf('/* PlotterPanels */') + 19); + + fileContent = firstPart + stringToInsert + secondPart; + } + } + } + } + } + } + } + } + + respondWithContent(fileContent, response); + + } + catch (err) { + console.log("[ERROR] server -> onBrowserRequest -> File Not Found: " + fileName + " or Error = " + err); + } + } + } + catch (err) { + console.log(err); + } + } else { + + respondWithFile('./Scripts/' + requestParameters[2], response); + + } + } + break; + + case "ExchangeAPI": // This is trying to access this library functionality from the broser. + { + + /* We are going to let access the exchange only to authenticated users, that measn that we need the a valid session token. */ + + let botDisplayName = requestParameters[2]; + let authToken = requestParameters[4]; + + if (authToken !== undefined) { + global.MARKET = { + assetA: "USDT", + assetB: "BTC" + }; + + const EXCHANGE_API = require('./Server/Exchange/ExchangeAPI'); + let exchangeAPI = EXCHANGE_API.newExchangeAPI(botDisplayName, authToken); + + exchangeAPI.initialize(onInizialized); + + function onInizialized(err) { + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> ExchangeAPI -> onInizialized -> Entering function."); } + + switch (err.result) { + case global.DEFAULT_OK_RESPONSE.result: { + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> ExchangeAPI -> onInizialized -> Execution finished well."); } + callExchangeAPIMethod(); + return; + } + case global.DEFAULT_RETRY_RESPONSE.result: { // Something bad happened, but if we retry in a while it might go through the next time. + console.log("[ERROR] server -> onBrowserRequest -> ExchangeAPI -> onInizialized -> Retry Later. Requesting Execution Retry."); + respondWithContent("", response); + return; + } + case global.DEFAULT_FAIL_RESPONSE.result: { // This is an unexpected exception that we do not know how to handle. + console.log("[ERROR] server -> onBrowserRequest -> ExchangeAPI -> onInizialized -> Operation Failed. Aborting the process."); + respondWithContent("", response); + return; + } + } + } + + function callExchangeAPIMethod() { + switch (requestParameters[3]) { + + case "getTicker": { + exchangeAPI.getTicker(global.MARKET, onExchangeResponse); + break; + } + + case "getOpenPositions": { + exchangeAPI.getOpenPositions(global.MARKET, onExchangeResponse); + break; + } + + case "getExecutedTrades": { + exchangeAPI.getExecutedTrades(requestParameters[5], onExchangeResponse); + break; + } + + case "putPosition": { + exchangeAPI.putPosition(global.MARKET, requestParameters[5], requestParameters[6], requestParameters[7], requestParameters[8], onExchangeResponse); + break; + } + + case "movePosition": { + exchangeAPI.moveOrder(requestParameters[5], requestParameters[6], requestParameters[7], onExchangeResponse); + break; + } + + case "getPublicTradeHistory": { + exchangeAPI.getPublicTradeHistory(global.MARKET, requestParameters[5], requestParameters[6], requestParameters[7], onExchangeResponse); + break; + } + + case "initialize": { + onExchangeResponse(global.DEFAULT_OK_RESPONSE); + break; + } + + } + } + + function onExchangeResponse(err, exchangeResponse) { + + /* Delete these secrets before they get logged. */ + + requestParameters[4] = ""; + if (err.result === global.DEFAULT_OK_RESPONSE.result) + respondWithContent(JSON.stringify(exchangeResponse), response); + else + respondWithContent(JSON.stringify(err), response); + } + + return; + } + + else { + + let customResponse = { + result: global.CUSTOM_FAIL_RESPONSE.result, + message: "You are not logged in." + }; + + respondWithContent(JSON.stringify(customResponse), response); + + } + } + break; + + case "AACloud": // This means the cloud folder. + { + + let filePath = requestParameters[2]; + + if (requestParameters[3] !== undefined) { + filePath = filePath + "/" + requestParameters[3] + } + + if (requestParameters[4] !== undefined) { + filePath = filePath + "/" + requestParameters[4] + } + + let map = storageData; + + let script = map.get(filePath); + + if (script !== undefined) { + + respondWithContent(script, response); + + } else { + + if (CONSOLE_LOG === true) { console.log("[WARN] server -> onBrowserRequest -> readEcosystemConfig -> Script Not Found."); } + if (CONSOLE_LOG === true) { console.log("[WARN] server -> onBrowserRequest -> readEcosystemConfig -> requestParameters[2] = " + requestParameters[2]); } + + respondWithContent("", response); + } + } + break; + + case "Bots": // This means the cloud folder. + { + switch (requestParameters[3]) { + + case 'bots': { + + let devTeam = requestParameters[2]; + let source = requestParameters[3]; + let repo = requestParameters[4]; + let path; + + if (requestParameters[6] !== undefined) { + path = requestParameters[5] + "/" + requestParameters[6]; + } else { + path = requestParameters[5]; + } + + botScripts.getScript(devTeam, source, repo, path, onScriptReady) + + break; + } + + case 'members': { + + let devTeam = requestParameters[2]; + let source = requestParameters[3] + "/" + requestParameters[4]; + let repo = requestParameters[5]; + let path; + + if (requestParameters[7] !== undefined) { + path = requestParameters[6] + "/" + requestParameters[7]; + } else { + path = requestParameters[6]; + } + + botScripts.getScript(devTeam, source, repo, path, onScriptReady) + + break; + } + + default : { + if (CONSOLE_LOG === true) { console.log("[ERROR] server -> onBrowserRequest -> Bots -> Invalid Request. "); } + } + } + + function onScriptReady(err, pScript) { + + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { + + console.log("[ERROR] server -> onBrowserRequest -> Bots -> onScriptReady -> Could not read a file. "); + console.log("[ERROR] server -> onBrowserRequest -> Bots -> onScriptReady -> err.message = " + err.message); + + pScript = "/* Your bot source code could not be retrieved. */"; + + } + + respondWithContent(pScript, response); + + } + } + break; + case "Plotters": // This means the plotter folder, not to be confused with the Plotters script! { - if (serverConfig.localMode === true) { + storage.readData(requestParameters[2] + "/" + "plotters", requestParameters[3], requestParameters[4], true, onDataArrived); - respondWithFile('../Plotters/' + requestParameters[2] + '/' + requestParameters[3] + '/' + requestParameters[4], response); + function onDataArrived(err, pData) { - } else { + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - getGithubData(requestParameters[2], requestParameters[3], requestParameters[4], onDataArrived) + console.log("[ERROR] server -> onBrowserRequest -> Plotters -> onDataArrived -> Could not read a file. "); + console.log("[ERROR] server -> onBrowserRequest -> Plotters -> onDataArrived -> err.message = " + err.message); - function onDataArrived(pData) { - - respondWithContent(pData, response); + pData = ""; } + + respondWithContent(pData, response); + } } - break; + break; case "PlotterPanels": // This means the PlotterPanels folder, not to be confused with the Plotter Panels scripts! { + storage.readData(requestParameters[2] + "/" + "plotters", requestParameters[3], requestParameters[4], true, onDataArrived); - if (serverConfig.localMode === true) { + function onDataArrived(err, pData) { - respondWithFile('../Plotters/' + requestParameters[2] + '/' + requestParameters[3] + '/' + requestParameters[4], response); + if (err.result !== global.DEFAULT_OK_RESPONSE.result) { - } else { + console.log("[ERROR] server -> onBrowserRequest -> Plotters -> PlotterPanels -> Could not read a file. "); + console.log("[ERROR] server -> onBrowserRequest -> Plotters -> PlotterPanels -> err.message = " + err.message); - getGithubData(requestParameters[2], requestParameters[3], requestParameters[4], onDataArrived) - - function onDataArrived(pData) { - - respondWithContent(pData, response); + pData = ""; } + + respondWithContent(pData, response); + } } - break; + break; case "Panels": { - respondWithFile('./' + requestParameters[1] + '/' + requestParameters[2], response); + respondWithFile(serverConfig.pathToCanvasApp + '/' + requestParameters[1] + '/' + requestParameters[2], response); } break; case "ChartLayers": { - respondWithFile('./' + requestParameters[1] + '/' + requestParameters[2], response); + respondWithFile(serverConfig.pathToCanvasApp + '/' + requestParameters[1] + '/' + requestParameters[2], response); } break; @@ -929,215 +1059,80 @@ function onBrowserRequest(request, response) { case "Spaces": { - respondWithFile('./' + requestParameters[1] + '/' + requestParameters[2], response); + respondWithFile(serverConfig.pathToCanvasApp + '/' + requestParameters[1] + '/' + requestParameters[2], response); + } + break; + + case "Scales": + { + respondWithFile(serverConfig.pathToCanvasApp + '/' + requestParameters[1] + '/' + requestParameters[2], response); } break; case "Files": { - respondWithFile('./' + requestParameters[1] + '/' + requestParameters[2], response); + respondWithFile(serverConfig.pathToCanvasApp + '/' + requestParameters[1] + '/' + requestParameters[2], response); } break; case "FloatingSpace": { - respondWithFile('./' + requestParameters[1] + '/' + requestParameters[2], response); + respondWithFile(serverConfig.pathToCanvasApp + '/' + requestParameters[1] + '/' + requestParameters[2], response); } break; default: + { + homePage(); + } + } - if (requestParameters[1] === "") { + function homePage() { - let fs = require('fs'); - try { - let fileName = 'index.html'; - fs.readFile(fileName, onFileRead); + if (requestParameters[1] === "") { - function onFileRead(err, file) { + let fs = require('fs'); + try { + let fileName = 'index.html'; + fs.readFile(fileName, onFileRead); - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> Entering function."); } + function onFileRead(err, file) { - try { + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> onFileRead -> Entering function."); } - let fileContent = file.toString(); + try { - addPlotters(); - addImages(); + let fileContent = file.toString(); - function addPlotters() { + /* The second request parameters is the sessionToken, if it exists. */ - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> addPlotters -> Entering function."); } + if (requestParameters[2] !== "" && requestParameters[2] !== undefined) { - let htmlLinePlotter = '' + '\n' + - ' ' - - let htmlLinePlotterPanel = '' + '\n' + - ' ' - - let devTeams = ecosystemObject.devTeams; - let hosts = ecosystemObject.hosts; - - addScript(devTeams); - addScript(hosts); - - function addScript(pDevTeamsOrHosts) { - - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> addPlotters -> addScript -> Entering function."); } - - for (let i = 0; i < pDevTeamsOrHosts.length; i++) { - - let devTeam = pDevTeamsOrHosts[i]; - - for (let j = 0; j < devTeam.plotters.length; j++) { - - let plotter = devTeam.plotters[j]; - - for (let k = 0; k < plotter.modules.length; k++) { - - let module = plotter.modules[k]; - - let htmlLineCopy = htmlLinePlotter; - - let stringToInsert; - stringToInsert = htmlLineCopy.replace('@devTeam@', devTeam.codeName); - stringToInsert = stringToInsert.replace('@repo@', plotter.repo); - stringToInsert = stringToInsert.replace('@module@', module.moduleName); - - let firstPart = fileContent.substring(0, fileContent.indexOf('') + 15); - let secondPart = fileContent.substring(fileContent.indexOf('') + 15); - - fileContent = firstPart + stringToInsert + secondPart; - - for (let l = 0; l < module.panels.length; l++) { - - let panel = module.panels[l]; - - let htmlLineCopy = htmlLinePlotterPanel; - - let stringToInsert; - stringToInsert = htmlLineCopy.replace('@devTeam@', devTeam.codeName); - stringToInsert = stringToInsert.replace('@repo@', plotter.repo); - stringToInsert = stringToInsert.replace('@module@', panel.moduleName); - - let firstPart = fileContent.substring(0, fileContent.indexOf('') + 20); - let secondPart = fileContent.substring(fileContent.indexOf('') + 20); - - fileContent = firstPart + stringToInsert + secondPart; - } - } - } - } - } - } - - function addImages() { - - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> addImages -> Entering function."); } - - const htmlLine = '' + '\n' + - ' ' - - let devTeams = ecosystemObject.devTeams; - - addScript(devTeams); - - function addScript(pDevTeams) { - - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> onFileRead -> addImages -> addScript -> Entering function."); } - - for (let i = 0; i < pDevTeams.length; i++) { - - let devTeam = pDevTeams[i]; - - let htmlLineCopy = htmlLine; - - let stringToInsert; - stringToInsert = htmlLineCopy.replace('@devTeam@', devTeam.codeName); - stringToInsert = stringToInsert.replace('@repo@', devTeam.codeName + "-Dev-Team"); - stringToInsert = stringToInsert.replace('@image@', devTeam.codeName + ".png"); - stringToInsert = stringToInsert.replace('@id@', devTeam.codeName + ".png"); - - let firstPart = fileContent.substring(0, fileContent.indexOf('') + 15); - let secondPart = fileContent.substring(fileContent.indexOf('') + 15); - - fileContent = firstPart + stringToInsert + secondPart; - - for (let j = 0; j < devTeam.bots.length; j++) { - - let bot = devTeam.bots[j]; - - if (bot.profilePicture !== undefined) { - - let htmlLineCopy = htmlLine; - - let stringToInsert; - stringToInsert = htmlLineCopy.replace('@devTeam@', devTeam.codeName); - stringToInsert = stringToInsert.replace('@repo@', bot.repo); - stringToInsert = stringToInsert.replace('@image@', bot.profilePicture); - stringToInsert = stringToInsert.replace('@id@', devTeam.codeName + "." + bot.profilePicture); - - let firstPart = fileContent.substring(0, fileContent.indexOf('') + 15); - let secondPart = fileContent.substring(fileContent.indexOf('') + 15); - - fileContent = firstPart + stringToInsert + secondPart; - - } - } - - for (let j = 0; j < devTeam.plotters.length; j++) { - - let plotter = devTeam.plotters[j]; - - for (let k = 0; k < plotter.modules.length; k++) { - - let module = plotter.modules[k]; - - if (module.profilePicture !== undefined) { - - let htmlLineCopy = htmlLine; - - let stringToInsert; - stringToInsert = htmlLineCopy.replace('@devTeam@', devTeam.codeName); - stringToInsert = stringToInsert.replace('@repo@', plotter.repo); - stringToInsert = stringToInsert.replace('@image@', module.profilePicture); - stringToInsert = stringToInsert.replace('@id@', devTeam.codeName + "." + plotter.codeName + "." + module.codeName + "." + module.profilePicture); - - let firstPart = fileContent.substring(0, fileContent.indexOf('') + 15); - let secondPart = fileContent.substring(fileContent.indexOf('') + 15); - - fileContent = firstPart + stringToInsert + secondPart; - - } - } - } - } - } - } - - - respondWithContent(fileContent, response); + fileContent = fileContent.replace("window.canvasApp.sessionToken = ''", "window.canvasApp.sessionToken = '" + requestParameters[2] + "'") } - catch (err) { - console.log("File Not Found: " + fileName + " or Error = " + err); - } + + respondWithContent(fileContent, response); } + catch (err) { + console.log("[ERROR] server -> onBrowserRequest -> File Not Found: " + fileName + " or Error = " + err); + } } - catch (err) { - console.log(err); - } - } else { - - respondWithFile("" + requestParameters[1], response); - } + catch (err) { + console.log(err); + } + } else { + + respondWithFile(serverConfig.pathToCanvasApp + '/' + requestParameters[1], response); + + } } function sendResponseToBrowser(htmlResponse) { - if (CONSOLE_LOG === true) { console.log("[INFO] onBrowserRequest -> sendResponseToBrowser -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> onBrowserRequest -> sendResponseToBrowser -> Entering function."); } response.writeHead(200, { 'Content-Type': 'text/html' }); response.write(htmlResponse); @@ -1148,7 +1143,7 @@ function onBrowserRequest(request, response) { function respondWithContent(content, response) { - if (CONSOLE_LOG === true) { console.log("[INFO] respondWithContent -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> respondWithContent -> Entering function."); } try { @@ -1164,13 +1159,13 @@ function respondWithContent(content, response) { } catch (err) { - returnEmptyArray(); + returnEmptyArray(response); } } function respondWithFile(fileName, response) { - if (CONSOLE_LOG === true) { console.log("[INFO] respondWithFile -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> respondWithFile -> Entering function."); } let fs = require('fs'); try { @@ -1179,7 +1174,7 @@ function respondWithFile(fileName, response) { function onFileRead(err, file) { - if (CONSOLE_LOG === true) { console.log("[INFO] respondWithFile -> onFileRead -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> respondWithFile -> onFileRead -> Entering function."); } try { let htmlResponse = file.toString(); @@ -1189,15 +1184,15 @@ function respondWithFile(fileName, response) { response.setHeader("Expires", "0"); // Proxies. response.setHeader("Access-Control-Allow-Origin", "*"); // Allows to access data from other domains. - response.writeHead(200, { 'Content-Type': 'text/html' }); + //response.writeHead(200, { 'Content-Type': 'text/html' }); response.write(htmlResponse); response.end("\n"); //console.log("File Sent: " + fileName); // } catch (err) { + console.log("[ERROR] server -> respondWithFile -> onFileRead -> File Not Found: " + fileName + " or Error = " + err); returnEmptyArray(); - console.log("File Not Found: " + fileName); } } @@ -1205,10 +1200,49 @@ function respondWithFile(fileName, response) { catch (err) { returnEmptyArray(); } +} - function returnEmptyArray() { +function respondWithImage(fileName, response) { - if (CONSOLE_LOG === true) { console.log("[INFO] respondWithFile -> returnEmptyArray -> Entering function."); } + if (CONSOLE_LOG === true) { console.log("[INFO] server -> respondWithImage -> Entering function."); } + + let fs = require('fs'); + try { + + fs.readFile(fileName, onFileRead); + + function onFileRead(err, file) { + + if (CONSOLE_LOG === true) { console.log("[INFO] server -> respondWithImage -> onFileRead -> Entering function."); } + + try { + + response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1. + response.setHeader("Pragma", "no-cache"); // HTTP 1.0. + response.setHeader("Expires", "0"); // Proxies. + response.setHeader("Access-Control-Allow-Origin", "*"); // Allows to access data from other domains. + + response.writeHead(200, { 'Content-Type': 'image/png' }); + response.end(file, 'binary'); + + } + catch (err) { + + console.log("[ERROR] server -> respondWithImage -> onFileRead -> File Not Found: " + fileName + " or Error = " + err); + } + + } + } + catch (err) { + console.log("[ERROR] server -> respondWithImage -> err = " + err); + } +} + +function returnEmptyArray(response) { + + try { + + if (CONSOLE_LOG === true) { console.log("[INFO] server -> respondWithFile -> returnEmptyArray -> Entering function."); } response.setHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1. response.setHeader("Pragma", "no-cache"); // HTTP 1.0. @@ -1218,97 +1252,33 @@ function respondWithFile(fileName, response) { response.write("[]"); response.end("\n"); + } catch (err) { + + console.log("[ERROR] server -> returnEmptyArray -> err.message " + err.message); + } } -function getGithubData(pOrg, pRepo, pPath, callBackFunction) { +function processPost(request, response, callback) { + let data = ""; + if (typeof callback !== 'function') return null; - if (CONSOLE_LOG === true) { console.log("[INFO] getGithubData -> Entering function."); } + if (request.method == 'POST') { + request.on('data', function (pData) { + data += pData; + if (data.length > 1e6) { + data = ""; + response.writeHead(413, { 'Content-Type': 'text/plain' }).end(); + request.connection.destroy(); + } + }); - let cacheVersion = githubData.get(pOrg + '.' + pRepo + '.' + pPath) - - if (cacheVersion !== undefined) { - - if (CONSOLE_LOG === true) { - - console.log("getGithubData - " + pOrg + '.' + pRepo + '.' + pPath + " found at cache. :-) "); - - } - - callBackFunction(cacheVersion); + request.on('end', function () { + callback(data); + }); } else { - - if (CONSOLE_LOG === true) { - - console.log("getGithubData - " + pOrg + '.' + pRepo + '.' + pPath + " NOT found at cache. :-( "); - - } - - const octokit = require('@octokit/rest')() - global.atob = require("atob"); - - let owner = pOrg; - let repo = pRepo; - let branch = "master"; - let page = 1; - let per_page = 100; - let ref = "master"; - let path = pPath; - - octokit.repos.getContent({ owner, repo, path, ref }, onContent); - - function onContent(error, result) { - - if (CONSOLE_LOG === true) { console.log("[INFO] getGithubData -> onContent -> Entering function."); } - if (CONSOLE_LOG === true) { console.log("[INFO] getGithubData -> onContent -> error = " + error); } - - if (CONSOLE_LOG === true) { - - console.log("[INFO] getGithubData -> onContent -> Github.com responded to request " + pOrg + '.' + pRepo + '.' + pPath + " with result = " + result.toString().substring(0,100)); - - } - - if (error !== null) { console.log("getGithubData -> onContent -> " + error);} - - let decoded = atob(result.data.content); - - /* - - This method usually brings up to 3 characters of encoding info at the begining of the JSON string which destroys the JSON format. - We will run the following code with the intention to eliminate this problem. - - */ - - let cleanString = decoded; - let jsonTest; - - try { - jsonTest = JSON.parse(cleanString); - } catch (err) { - cleanString = decoded.substring(1); - try { - jsonTest = JSON.parse(cleanString); - } catch (err) { - cleanString = decoded.substring(2); - try { - jsonTest = JSON.parse(cleanString); - } catch (err) { - cleanString = decoded.substring(3); - try { - jsonTest = JSON.parse(cleanString); - } catch (err) { - console.log("getGithubData -> onContent -> Could not clean the data received -> Data = " + decoded.substring(0, 50)); - } - } - } - } - - githubData.set(pOrg + '.' + pRepo + '.' + pPath, cleanString); - - callBackFunction(cleanString); - - } + response.writeHead(405, { 'Content-Type': 'text/plain' }); + response.end(); } } - diff --git a/this.server.config.json b/this.server.config.json deleted file mode 100644 index 2d2579a0a..000000000 --- a/this.server.config.json +++ /dev/null @@ -1,4 +0,0 @@ -{ - "localMode": true, - "ambient": "Develop" -} \ No newline at end of file